asterisk-1.4.21.2/0000755000175000017500000000000011162660652013033 5ustar maniacmaniacasterisk-1.4.21.2/Makefile0000644000175000017500000007164011007665563014507 0ustar maniacmaniac# # Asterisk -- A telephony toolkit for Linux. # # Top level Makefile # # Copyright (C) 1999-2006, Digium, Inc. # # Mark Spencer # # This program is free software, distributed under the terms of # the GNU General Public License # # All Makefiles use the following variables: # # ASTCFLAGS - compiler options # ASTLDFLAGS - linker flags (not libraries) # AST_LIBS - libraries to build binaries XXX # LIBS - additional libraries, at top-level for all links, # on a single object just for that object # SOLINK - linker flags used only for creating shared objects (.so files), # used for all .so links # # Default values fo ASTCFLAGS and ASTLDFLAGS can be specified in the # environment when running make, as follows: # # $ ASTCFLAGS="-Werror" make export ASTTOPDIR export ASTERISKVERSION export ASTERISKVERSIONNUM export INSTALL_PATH export ASTETCDIR export ASTVARRUNDIR export MODULES_DIR export ASTSPOOLDIR export ASTVARLIBDIR export ASTDATADIR export ASTLOGDIR export ASTLIBDIR export ASTMANDIR export ASTHEADERDIR export ASTBINDIR export ASTSBINDIR export AGI_DIR export ASTCONFPATH export NOISY_BUILD export MENUSELECT_CFLAGS export CC export CXX export AR export RANLIB export HOST_CC export STATIC_BUILD export INSTALL export DESTDIR export PROC export SOLINK export STRIP export DOWNLOAD export AWK export GREP export ID export OSARCH export CURSES_DIR export NCURSES_DIR export TERMCAP_DIR export TINFO_DIR export GTK2_LIB export GTK2_INCLUDE # even though we could use '-include makeopts' here, use a wildcard # lookup anyway, so that make won't try to build makeopts if it doesn't # exist (other rules will force it to be built if needed) ifneq ($(wildcard makeopts),) include makeopts endif # Some build systems, such as the one in openwrt, like to pass custom target # CFLAGS and LDFLAGS in the COPTS and LDOPTS variables. ASTCFLAGS+=$(COPTS) ASTLDFLAGS+=$(LDOPTS) #Uncomment this to see all build commands instead of 'quiet' output #NOISY_BUILD=yes # Create OPTIONS variable OPTIONS= empty:= space:=$(empty) $(empty) ASTTOPDIR:=$(subst $(space),\$(space),$(CURDIR)) # Overwite config files on "make samples" OVERWRITE=y # Include debug and macro symbols in the executables (-g) and profiling info (-pg) DEBUG=-g3 # Staging directory # Files are copied here temporarily during the install process # For example, make DESTDIR=/tmp/asterisk woud put things in # /tmp/asterisk/etc/asterisk # !!! Watch out, put no spaces or comments after the value !!! #DESTDIR?=/tmp/asterisk # Define standard directories for various platforms # These apply if they are not redefined in asterisk.conf ifeq ($(OSARCH),SunOS) ASTETCDIR=/var/etc/asterisk ASTLIBDIR=/opt/asterisk/lib ASTVARLIBDIR=/var/opt/asterisk ASTSPOOLDIR=/var/spool/asterisk ASTLOGDIR=/var/log/asterisk ASTHEADERDIR=/opt/asterisk/include ASTBINDIR=/opt/asterisk/bin ASTSBINDIR=/opt/asterisk/sbin ASTVARRUNDIR=/var/run/asterisk ASTMANDIR=/opt/asterisk/man else ASTETCDIR=$(sysconfdir)/asterisk ASTLIBDIR=$(libdir)/asterisk ASTHEADERDIR=$(includedir)/asterisk ASTBINDIR=$(bindir) ASTSBINDIR=$(sbindir) ASTSPOOLDIR=$(localstatedir)/spool/asterisk ASTLOGDIR=$(localstatedir)/log/asterisk ASTVARRUNDIR=$(localstatedir)/run ASTMANDIR=$(mandir) ifneq ($(findstring BSD,$(OSARCH)),) ASTVARLIBDIR=$(prefix)/share/asterisk ASTVARRUNDIR=$(localstatedir)/run/asterisk else ASTVARLIBDIR=$(localstatedir)/lib/asterisk endif endif ifeq ($(ASTDATADIR),) ASTDATADIR:=$(ASTVARLIBDIR) endif # Asterisk.conf is located in ASTETCDIR or by using the -C flag # when starting Asterisk ASTCONFPATH=$(ASTETCDIR)/asterisk.conf MODULES_DIR=$(ASTLIBDIR)/modules AGI_DIR=$(ASTDATADIR)/agi-bin # If you use Apache, you may determine by a grep 'DocumentRoot' of your httpd.conf file HTTP_DOCSDIR=/var/www/html # Determine by a grep 'ScriptAlias' of your Apache httpd.conf file HTTP_CGIDIR=/var/www/cgi-bin # Uncomment this to use the older DSP routines #ASTCFLAGS+=-DOLD_DSP_ROUTINES # If the file .asterisk.makeopts is present in your home directory, you can # include all of your favorite menuselect options so that every time you download # a new version of Asterisk, you don't have to run menuselect to set them. # The file /etc/asterisk.makeopts will also be included but can be overridden # by the file in your home directory. GLOBAL_MAKEOPTS=$(wildcard /etc/asterisk.makeopts) USER_MAKEOPTS=$(wildcard ~/.asterisk.makeopts) MOD_SUBDIR_CFLAGS=-I$(ASTTOPDIR)/include OTHER_SUBDIR_CFLAGS=-I$(ASTTOPDIR)/include ifeq ($(OSARCH),linux-gnu) ifeq ($(PROC),x86_64) # You must have GCC 3.4 to use k8, otherwise use athlon PROC=k8 #PROC=athlon endif ifeq ($(PROC),sparc64) #The problem with sparc is the best stuff is in newer versions of gcc (post 3.0) only. #This works for even old (2.96) versions of gcc and provides a small boost either way. #A ultrasparc cpu is really v9 but the stock debian stable 3.0 gcc doesn't support it. #So we go lowest common available by gcc and go a step down, still a step up from #the default as we now have a better instruction set to work with. - Belgarath PROC=ultrasparc OPTIONS+=$(shell if $(CC) -mtune=$(PROC) -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-mtune=$(PROC)"; fi) OPTIONS+=$(shell if $(CC) -mcpu=v8 -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-mcpu=v8"; fi) OPTIONS+=-fomit-frame-pointer endif ifeq ($(PROC),arm) # The Cirrus logic is the only heavily shipping arm processor with a real floating point unit ifeq ($(SUB_PROC),maverick) OPTIONS+=-fsigned-char -mcpu=ep9312 else ifeq ($(SUB_PROC),xscale) OPTIONS+=-fsigned-char -mcpu=xscale else OPTIONS+=-fsigned-char endif endif endif endif ifeq ($(findstring -save-temps,$(ASTCFLAGS)),) ASTCFLAGS+=-pipe endif ASTCFLAGS+=-Wall -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations $(DEBUG) ASTCFLAGS+=-include $(ASTTOPDIR)/include/asterisk/autoconfig.h ifeq ($(AST_DEVMODE),yes) ASTCFLAGS+=-Werror -Wunused $(AST_DECLARATION_AFTER_STATEMENT) endif ifneq ($(findstring BSD,$(OSARCH)),) ASTCFLAGS+=-I/usr/local/include ASTLDFLAGS+=-L/usr/local/lib endif ifneq ($(PROC),ultrasparc) ASTCFLAGS+=$(shell if $(CC) -march=$(PROC) -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=$(PROC)"; fi) endif ifeq ($(PROC),ppc) ASTCFLAGS+=-fsigned-char endif ifeq ($(OSARCH),FreeBSD) # -V is understood by BSD Make, not by GNU make. BSDVERSION=$(shell make -V OSVERSION -f /usr/share/mk/bsd.port.subdir.mk) ASTCFLAGS+=$(shell if test $(BSDVERSION) -lt 500016 ; then echo "-D_THREAD_SAFE"; fi) AST_LIBS+=$(shell if test $(BSDVERSION) -lt 502102 ; then echo "-lc_r"; else echo "-pthread"; fi) endif ifeq ($(OSARCH),NetBSD) ASTCFLAGS+=-pthread -I/usr/pkg/include endif ifeq ($(OSARCH),OpenBSD) ASTCFLAGS+=-pthread endif ifeq ($(OSARCH),SunOS) ASTCFLAGS+=-Wcast-align -DSOLARIS -I../include/solaris-compat -I/opt/ssl/include -I/usr/local/ssl/include endif ASTERISKVERSION:=$(shell GREP=$(GREP) AWK=$(AWK) build_tools/make_version .) ifneq ($(wildcard .version),) ASTERISKVERSIONNUM:=$(shell $(AWK) -F. '{printf "%01d%02d%02d", $$1, $$2, $$3}' .version) RPMVERSION:=$(shell sed 's/[-\/:]/_/g' .version) else RPMVERSION=unknown endif ifneq ($(wildcard .svn),) ASTERISKVERSIONNUM=999999 endif ASTCFLAGS+=$(MALLOC_DEBUG)$(BUSYDETECT)$(OPTIONS) MOD_SUBDIRS:=res channels pbx apps codecs formats cdr funcs main OTHER_SUBDIRS:=utils agi SUBDIRS:=$(OTHER_SUBDIRS) $(MOD_SUBDIRS) SUBDIRS_INSTALL:=$(SUBDIRS:%=%-install) SUBDIRS_CLEAN:=$(SUBDIRS:%=%-clean) SUBDIRS_DIST_CLEAN:=$(SUBDIRS:%=%-dist-clean) SUBDIRS_UNINSTALL:=$(SUBDIRS:%=%-uninstall) MOD_SUBDIRS_EMBED_LDSCRIPT:=$(MOD_SUBDIRS:%=%-embed-ldscript) MOD_SUBDIRS_EMBED_LDFLAGS:=$(MOD_SUBDIRS:%=%-embed-ldflags) MOD_SUBDIRS_EMBED_LIBS:=$(MOD_SUBDIRS:%=%-embed-libs) MOD_SUBDIRS_MENUSELECT_TREE:=$(MOD_SUBDIRS:%=%-menuselect-tree) ifneq ($(findstring darwin,$(OSARCH)),) ASTCFLAGS+=-D__Darwin__ AUDIO_LIBS=-framework CoreAudio SOLINK=-dynamic -bundle -undefined suppress -force_flat_namespace else # These are used for all but Darwin SOLINK=-shared -Xlinker -x ifneq ($(findstring BSD,$(OSARCH)),) LDFLAGS+=-L/usr/local/lib endif endif ifeq ($(OSARCH),SunOS) SOLINK=-shared -fpic -L/usr/local/ssl/lib endif SUBMAKE=$(MAKE) --quiet --no-print-directory # This is used when generating the doxygen documentation ifneq ($(DOT),:) HAVEDOT=yes else HAVEDOT=no endif all: _all @echo " +--------- Asterisk Build Complete ---------+" @echo " + Asterisk has successfully been built, and +" @echo " + can be installed by running: +" @echo " + +" ifeq ($(MAKE), gmake) @echo " + $(MAKE) install +" else @echo " + $(MAKE) install +" endif @echo " +-------------------------------------------+" _all: cleantest $(SUBDIRS) makeopts: configure @echo "****" @echo "**** The configure script must be executed before running '$(MAKE)'." @echo "**** Please run \"./configure\"." @echo "****" @exit 1 menuselect.makeopts: menuselect/menuselect menuselect-tree menuselect/menuselect --check-deps menuselect.makeopts $(GLOBAL_MAKEOPTS) $(USER_MAKEOPTS) $(MOD_SUBDIRS_EMBED_LDSCRIPT): @echo "EMBED_LDSCRIPTS+="`$(SUBMAKE) -C $(@:-embed-ldscript=) SUBDIR=$(@:-embed-ldscript=) __embed_ldscript` >> makeopts.embed_rules $(MOD_SUBDIRS_EMBED_LDFLAGS): @echo "EMBED_LDFLAGS+="`$(SUBMAKE) -C $(@:-embed-ldflags=) SUBDIR=$(@:-embed-ldflags=) __embed_ldflags` >> makeopts.embed_rules $(MOD_SUBDIRS_EMBED_LIBS): @echo "EMBED_LIBS+="`$(SUBMAKE) -C $(@:-embed-libs=) SUBDIR=$(@:-embed-libs=) __embed_libs` >> makeopts.embed_rules $(MOD_SUBDIRS_MENUSELECT_TREE): @$(SUBMAKE) -C $(@:-menuselect-tree=) SUBDIR=$(@:-menuselect-tree=) moduleinfo @$(SUBMAKE) -C $(@:-menuselect-tree=) SUBDIR=$(@:-menuselect-tree=) makeopts makeopts.embed_rules: menuselect.makeopts @echo "Generating embedded module rules ..." @rm -f $@ @$(MAKE) --no-print-directory $(MOD_SUBDIRS_EMBED_LDSCRIPT) @$(MAKE) --no-print-directory $(MOD_SUBDIRS_EMBED_LDFLAGS) @$(MAKE) --no-print-directory $(MOD_SUBDIRS_EMBED_LIBS) $(SUBDIRS): include/asterisk/version.h include/asterisk/buildopts.h defaults.h makeopts.embed_rules # ensure that all module subdirectories are processed before 'main' during # a parallel build, since if there are modules selected to be embedded the # directories containing them must be completed before the main Asterisk # binary can be built main: $(filter-out main,$(MOD_SUBDIRS)) $(MOD_SUBDIRS): @ASTCFLAGS="$(MOD_SUBDIR_CFLAGS) $(ASTCFLAGS)" ASTLDFLAGS="$(ASTLDFLAGS)" AST_LIBS="$(AST_LIBS)" $(MAKE) --no-print-directory --no-builtin-rules -C $@ SUBDIR=$@ all $(OTHER_SUBDIRS): @ASTCFLAGS="$(OTHER_SUBDIR_CFLAGS) $(ASTCFLAGS)" ASTLDFLAGS="$(ASTLDFLAGS)" AUDIO_LIBS="$(AUDIO_LIBS)" $(MAKE) --no-print-directory --no-builtin-rules -C $@ SUBDIR=$@ all defaults.h: makeopts @build_tools/make_defaults_h > $@.tmp @if cmp -s $@.tmp $@ ; then : ; else \ mv $@.tmp $@ ; \ fi @rm -f $@.tmp include/asterisk/version.h: @build_tools/make_version_h > $@.tmp @if cmp -s $@.tmp $@ ; then : ; else \ mv $@.tmp $@ ; \ fi @rm -f $@.tmp include/asterisk/buildopts.h: menuselect.makeopts @build_tools/make_buildopts_h > $@.tmp @if cmp -s $@.tmp $@ ; then : ; else \ mv $@.tmp $@ ; \ fi @rm -f $@.tmp $(SUBDIRS_CLEAN): @$(MAKE) --no-print-directory -C $(@:-clean=) clean $(SUBDIRS_DIST_CLEAN): @$(MAKE) --no-print-directory -C $(@:-dist-clean=) dist-clean clean: $(SUBDIRS_CLEAN) rm -f defaults.h rm -f include/asterisk/build.h rm -f include/asterisk/version.h @$(MAKE) -C menuselect clean cp -f .cleancount .lastclean dist-clean: distclean distclean: $(SUBDIRS_DIST_CLEAN) clean @$(MAKE) -C menuselect dist-clean @$(MAKE) -C sounds dist-clean rm -f menuselect.makeopts makeopts menuselect-tree menuselect.makedeps rm -f makeopts.embed_rules rm -f config.log config.status rm -rf autom4te.cache rm -f include/asterisk/autoconfig.h rm -f include/asterisk/buildopts.h rm -rf doc/api rm -f build_tools/menuselect-deps datafiles: _all if [ x`$(ID) -un` = xroot ]; then CFLAGS="$(ASTCFLAGS)" sh build_tools/mkpkgconfig $(DESTDIR)/usr/lib/pkgconfig; fi # Should static HTTP be installed during make samples or even with its own target ala # webvoicemail? There are portions here that *could* be customized but might also be # improved a lot. I'll put it here for now. mkdir -p $(DESTDIR)$(ASTDATADIR)/static-http for x in static-http/*; do \ $(INSTALL) -m 644 $$x $(DESTDIR)$(ASTDATADIR)/static-http ; \ done mkdir -p $(DESTDIR)$(ASTDATADIR)/images for x in images/*.jpg; do \ $(INSTALL) -m 644 $$x $(DESTDIR)$(ASTDATADIR)/images ; \ done mkdir -p $(DESTDIR)$(AGI_DIR) $(MAKE) -C sounds install update: @if [ -d .svn ]; then \ echo "Updating from Subversion..." ; \ svn update | tee update.out; \ rm -f .version; \ if [ `grep -c ^C update.out` -gt 0 ]; then \ echo ; echo "The following files have conflicts:" ; \ grep ^C update.out | cut -b4- ; \ fi ; \ rm -f update.out; \ else \ echo "Not under version control"; \ fi NEWHEADERS=$(notdir $(wildcard include/asterisk/*.h)) OLDHEADERS=$(filter-out $(NEWHEADERS),$(notdir $(wildcard $(DESTDIR)$(ASTHEADERDIR)/*.h))) installdirs: mkdir -p $(DESTDIR)$(MODULES_DIR) mkdir -p $(DESTDIR)$(ASTSBINDIR) mkdir -p $(DESTDIR)$(ASTETCDIR) mkdir -p $(DESTDIR)$(ASTBINDIR) mkdir -p $(DESTDIR)$(ASTVARRUNDIR) mkdir -p $(DESTDIR)$(ASTSPOOLDIR)/voicemail mkdir -p $(DESTDIR)$(ASTSPOOLDIR)/dictate mkdir -p $(DESTDIR)$(ASTSPOOLDIR)/system mkdir -p $(DESTDIR)$(ASTSPOOLDIR)/tmp mkdir -p $(DESTDIR)$(ASTSPOOLDIR)/meetme mkdir -p $(DESTDIR)$(ASTSPOOLDIR)/monitor bininstall: _all installdirs $(SUBDIRS_INSTALL) $(INSTALL) -m 755 main/asterisk $(DESTDIR)$(ASTSBINDIR)/ $(LN) -sf asterisk $(DESTDIR)$(ASTSBINDIR)/rasterisk $(INSTALL) -m 755 contrib/scripts/astgenkey $(DESTDIR)$(ASTSBINDIR)/ $(INSTALL) -m 755 contrib/scripts/autosupport $(DESTDIR)$(ASTSBINDIR)/ if [ ! -f $(DESTDIR)$(ASTSBINDIR)/safe_asterisk ]; then \ cat contrib/scripts/safe_asterisk | sed 's|__ASTERISK_SBIN_DIR__|$(ASTSBINDIR)|;s|__ASTERISK_VARRUN_DIR__|$(ASTVARRUNDIR)|;' > $(DESTDIR)$(ASTSBINDIR)/safe_asterisk ;\ chmod 755 $(DESTDIR)$(ASTSBINDIR)/safe_asterisk;\ fi $(INSTALL) -d $(DESTDIR)$(ASTHEADERDIR) $(INSTALL) -m 644 include/asterisk.h $(DESTDIR)$(includedir) $(INSTALL) -m 644 include/asterisk/*.h $(DESTDIR)$(ASTHEADERDIR) if [ -n "$(OLDHEADERS)" ]; then \ rm -f $(addprefix $(DESTDIR)$(ASTHEADERDIR)/,$(OLDHEADERS)) ;\ fi mkdir -p $(DESTDIR)$(ASTLOGDIR)/cdr-csv mkdir -p $(DESTDIR)$(ASTLOGDIR)/cdr-custom mkdir -p $(DESTDIR)$(ASTDATADIR)/keys mkdir -p $(DESTDIR)$(ASTDATADIR)/firmware mkdir -p $(DESTDIR)$(ASTDATADIR)/firmware/iax mkdir -p $(DESTDIR)$(ASTMANDIR)/man8 $(INSTALL) -m 644 keys/iaxtel.pub $(DESTDIR)$(ASTDATADIR)/keys $(INSTALL) -m 644 keys/freeworlddialup.pub $(DESTDIR)$(ASTDATADIR)/keys $(INSTALL) -m 644 doc/asterisk.8 $(DESTDIR)$(ASTMANDIR)/man8 $(INSTALL) -m 644 contrib/scripts/astgenkey.8 $(DESTDIR)$(ASTMANDIR)/man8 $(INSTALL) -m 644 contrib/scripts/autosupport.8 $(DESTDIR)$(ASTMANDIR)/man8 $(INSTALL) -m 644 contrib/scripts/safe_asterisk.8 $(DESTDIR)$(ASTMANDIR)/man8 if [ -f contrib/firmware/iax/iaxy.bin ] ; then \ $(INSTALL) -m 644 contrib/firmware/iax/iaxy.bin $(DESTDIR)$(ASTDATADIR)/firmware/iax/iaxy.bin; \ fi $(SUBDIRS_INSTALL): @DESTDIR="$(DESTDIR)" ASTSBINDIR="$(ASTSBINDIR)" $(MAKE) -C $(@:-install=) install NEWMODS=$(notdir $(wildcard */*.so)) OLDMODS=$(filter-out $(NEWMODS),$(notdir $(wildcard $(DESTDIR)$(MODULES_DIR)/*.so))) oldmodcheck: @if [ -n "$(OLDMODS)" ]; then \ echo " WARNING WARNING WARNING" ;\ echo "" ;\ echo " Your Asterisk modules directory, located at" ;\ echo " $(DESTDIR)$(MODULES_DIR)" ;\ echo " contains modules that were not installed by this " ;\ echo " version of Asterisk. Please ensure that these" ;\ echo " modules are compatible with this version before" ;\ echo " attempting to run Asterisk." ;\ echo "" ;\ for f in $(OLDMODS); do \ echo " $$f" ;\ done ;\ echo "" ;\ echo " WARNING WARNING WARNING" ;\ fi badshell: ifneq ($(findstring ~,$(DESTDIR)),) @echo "Your shell doesn't do ~ expansion when expected (specifically, when doing \"make install DESTDIR=~/path\")." @echo "Try replacing ~ with \$$HOME, as in \"make install DESTDIR=\$$HOME/path\"." @exit 1 endif install: badshell datafiles bininstall @if [ -x /usr/sbin/asterisk-post-install ]; then \ /usr/sbin/asterisk-post-install $(DESTDIR) . ; \ fi @echo " +---- Asterisk Installation Complete -------+" @echo " + +" @echo " + YOU MUST READ THE SECURITY DOCUMENT +" @echo " + +" @echo " + Asterisk has successfully been installed. +" @echo " + If you would like to install the sample +" @echo " + configuration files (overwriting any +" @echo " + existing config files), run: +" @echo " + +" ifeq ($(MAKE), gmake) @echo " + $(MAKE) samples +" else @echo " + $(MAKE) samples +" endif @echo " + +" @echo " +----------------- or ---------------------+" @echo " + +" @echo " + You can go ahead and install the asterisk +" @echo " + program documentation now or later run: +" @echo " + +" ifeq ($(MAKE), gmake) @echo " + $(MAKE) progdocs +" else @echo " + $(MAKE) progdocs +" endif @echo " + +" @echo " + **Note** This requires that you have +" @echo " + doxygen installed on your local system +" @echo " +-------------------------------------------+" @$(MAKE) -s oldmodcheck upgrade: bininstall adsi: mkdir -p $(DESTDIR)$(ASTETCDIR) for x in configs/*.adsi; do \ if [ ! -f $(DESTDIR)$(ASTETCDIR)/$$x ]; then \ $(INSTALL) -m 644 $$x $(DESTDIR)$(ASTETCDIR)/`$(BASENAME) $$x` ; \ fi ; \ done samples: adsi mkdir -p $(DESTDIR)$(ASTETCDIR) for x in configs/*.sample; do \ if [ -f $(DESTDIR)$(ASTETCDIR)/`$(BASENAME) $$x .sample` ]; then \ if [ "$(OVERWRITE)" = "y" ]; then \ if cmp -s $(DESTDIR)$(ASTETCDIR)/`$(BASENAME) $$x .sample` $$x ; then \ echo "Config file $$x is unchanged"; \ continue; \ fi ; \ mv -f $(DESTDIR)$(ASTETCDIR)/`$(BASENAME) $$x .sample` $(DESTDIR)$(ASTETCDIR)/`$(BASENAME) $$x .sample`.old ; \ else \ echo "Skipping config file $$x"; \ continue; \ fi ;\ fi ; \ $(INSTALL) -m 644 $$x $(DESTDIR)$(ASTETCDIR)/`$(BASENAME) $$x .sample` ;\ done if [ "$(OVERWRITE)" = "y" ] || [ ! -f $(DESTDIR)$(ASTCONFPATH) ]; then \ ( \ echo "[directories]" ; \ echo "astetcdir => $(ASTETCDIR)" ; \ echo "astmoddir => $(MODULES_DIR)" ; \ echo "astvarlibdir => $(ASTVARLIBDIR)" ; \ echo "astdatadir => $(ASTDATADIR)" ; \ echo "astagidir => $(AGI_DIR)" ; \ echo "astspooldir => $(ASTSPOOLDIR)" ; \ echo "astrundir => $(ASTVARRUNDIR)" ; \ echo "astlogdir => $(ASTLOGDIR)" ; \ echo "" ; \ echo ";[options]" ; \ echo ";verbose = 3" ; \ echo ";debug = 3" ; \ echo ";alwaysfork = yes ; same as -F at startup" ; \ echo ";nofork = yes ; same as -f at startup" ; \ echo ";quiet = yes ; same as -q at startup" ; \ echo ";timestamp = yes ; same as -T at startup" ; \ echo ";execincludes = yes ; support #exec in config files" ; \ echo ";console = yes ; Run as console (same as -c at startup)" ; \ echo ";highpriority = yes ; Run realtime priority (same as -p at startup)" ; \ echo ";initcrypto = yes ; Initialize crypto keys (same as -i at startup)" ; \ echo ";nocolor = yes ; Disable console colors" ; \ echo ";dontwarn = yes ; Disable some warnings" ; \ echo ";dumpcore = yes ; Dump core on crash (same as -g at startup)" ; \ echo ";languageprefix = yes ; Use the new sound prefix path syntax" ; \ echo ";internal_timing = yes" ; \ echo ";systemname = my_system_name ; prefix uniqueid with a system name for global uniqueness issues" ; \ echo ";maxcalls = 10 ; Maximum amount of calls allowed" ; \ echo ";maxload = 0.9 ; Asterisk stops accepting new calls if the load average exceed this limit" ; \ echo ";cache_record_files = yes ; Cache recorded sound files to another directory during recording" ; \ echo ";record_cache_dir = /tmp ; Specify cache directory (used in cnjunction with cache_record_files)" ; \ echo ";transmit_silence_during_record = yes ; Transmit SLINEAR silence while a channel is being recorded" ; \ echo ";transmit_silence = yes ; Transmit SLINEAR silence while a channel is being recorded or DTMF is being generated" ; \ echo ";transcode_via_sln = yes ; Build transcode paths via SLINEAR, instead of directly" ; \ echo ";runuser = asterisk ; The user to run as" ; \ echo ";rungroup = asterisk ; The group to run as" ; \ echo "" ; \ echo "; Changing the following lines may compromise your security." ; \ echo ";[files]" ; \ echo ";astctlpermissions = 0660" ; \ echo ";astctlowner = root" ; \ echo ";astctlgroup = apache" ; \ echo ";astctl = asterisk.ctl" ; \ ) > $(DESTDIR)$(ASTCONFPATH) ; \ else \ echo "Skipping asterisk.conf creation"; \ fi mkdir -p $(DESTDIR)$(ASTSPOOLDIR)/voicemail/default/1234/INBOX build_tools/make_sample_voicemail $(DESTDIR)/$(ASTDATADIR) $(DESTDIR)/$(ASTSPOOLDIR) webvmail: @[ -d $(DESTDIR)$(HTTP_DOCSDIR)/ ] || ( printf "http docs directory not found.\nUpdate assignment of variable HTTP_DOCSDIR in Makefile!\n" && exit 1 ) @[ -d $(DESTDIR)$(HTTP_CGIDIR) ] || ( printf "cgi-bin directory not found.\nUpdate assignment of variable HTTP_CGIDIR in Makefile!\n" && exit 1 ) $(INSTALL) -m 4755 -o root -g root contrib/scripts/vmail.cgi $(DESTDIR)$(HTTP_CGIDIR)/vmail.cgi mkdir -p $(DESTDIR)$(HTTP_DOCSDIR)/_asterisk for x in images/*.gif; do \ $(INSTALL) -m 644 $$x $(DESTDIR)$(HTTP_DOCSDIR)/_asterisk/; \ done @echo " +--------- Asterisk Web Voicemail ----------+" @echo " + +" @echo " + Asterisk Web Voicemail is installed in +" @echo " + your cgi-bin directory: +" @echo " + $(DESTDIR)$(HTTP_CGIDIR)" @echo " + IT USES A SETUID ROOT PERL SCRIPT, SO +" @echo " + IF YOU DON'T LIKE THAT, UNINSTALL IT! +" @echo " + +" @echo " + Other static items have been stored in: +" @echo " + $(DESTDIR)$(HTTP_DOCSDIR)" @echo " + +" @echo " + If these paths do not match your httpd +" @echo " + installation, correct the definitions +" @echo " + in your Makefile of HTTP_CGIDIR and +" @echo " + HTTP_DOCSDIR +" @echo " + +" @echo " +-------------------------------------------+" spec: sed "s/^Version:.*/Version: $(RPMVERSION)/g" redhat/asterisk.spec > asterisk.spec ; \ rpm: __rpm __rpm: include/asterisk/version.h include/asterisk/buildopts.h spec rm -rf /tmp/asterisk ; \ mkdir -p /tmp/asterisk/redhat/RPMS/i386 ; \ $(MAKE) DESTDIR=/tmp/asterisk install ; \ $(MAKE) DESTDIR=/tmp/asterisk samples ; \ mkdir -p /tmp/asterisk/etc/rc.d/init.d ; \ cp -f contrib/init.d/rc.redhat.asterisk /tmp/asterisk/etc/rc.d/init.d/asterisk ; \ rpmbuild --rcfile /usr/lib/rpm/rpmrc:redhat/rpmrc -bb asterisk.spec progdocs: (cat contrib/asterisk-ng-doxygen; echo "HAVE_DOT=$(HAVEDOT)"; \ echo "PROJECT_NUMBER=$(ASTERISKVERSION)") | doxygen - config: @if [ "${OSARCH}" = "linux-gnu" ]; then \ if [ -f /etc/redhat-release -o -f /etc/fedora-release ]; then \ $(INSTALL) -m 755 contrib/init.d/rc.redhat.asterisk $(DESTDIR)/etc/rc.d/init.d/asterisk; \ if [ -z "$(DESTDIR)" ]; then /sbin/chkconfig --add asterisk; fi; \ elif [ -f /etc/debian_version ]; then \ $(INSTALL) -m 755 contrib/init.d/rc.debian.asterisk $(DESTDIR)/etc/init.d/asterisk; \ if [ -z "$(DESTDIR)" ]; then /usr/sbin/update-rc.d asterisk start 50 2 3 4 5 . stop 91 2 3 4 5 .; fi; \ elif [ -f /etc/gentoo-release ]; then \ $(INSTALL) -m 755 contrib/init.d/rc.gentoo.asterisk $(DESTDIR)/etc/init.d/asterisk; \ if [ -z "$(DESTDIR)" ]; then /sbin/rc-update add asterisk default; fi; \ elif [ -f /etc/mandrake-release -o -f /etc/mandriva-release ]; then \ $(INSTALL) -m 755 contrib/init.d/rc.mandrake.asterisk $(DESTDIR)/etc/rc.d/init.d/asterisk; \ if [ -z "$(DESTDIR)" ]; then /sbin/chkconfig --add asterisk; fi; \ elif [ -f /etc/SuSE-release -o -f /etc/novell-release ]; then \ $(INSTALL) -m 755 contrib/init.d/rc.suse.asterisk $(DESTDIR)/etc/init.d/asterisk; \ if [ -z "$(DESTDIR)" ]; then /sbin/chkconfig --add asterisk; fi; \ elif [ -f /etc/slackware-version ]; then \ echo "Slackware is not currently supported, although an init script does exist for it." \ else \ echo "We could not install init scripts for your distribution."; \ fi \ else \ echo "We could not install init scripts for your operating system."; \ fi sounds: $(MAKE) -C sounds all # If the cleancount has been changed, force a make clean. # .cleancount is the global clean count, and .lastclean is the # last clean count we had cleantest: @cmp -s .cleancount .lastclean || $(MAKE) clean $(SUBDIRS_UNINSTALL): @$(MAKE) --no-print-directory -C $(@:-uninstall=) uninstall _uninstall: $(SUBDIRS_UNINSTALL) rm -f $(DESTDIR)$(MODULES_DIR)/* rm -f $(DESTDIR)$(ASTSBINDIR)/*asterisk* rm -f $(DESTDIR)$(ASTSBINDIR)/astgenkey rm -f $(DESTDIR)$(ASTSBINDIR)/autosupport rm -rf $(DESTDIR)$(ASTHEADERDIR) rm -rf $(DESTDIR)$(ASTDATADIR)/firmware rm -f $(DESTDIR)$(ASTMANDIR)/man8/asterisk.8 rm -f $(DESTDIR)$(ASTMANDIR)/man8/astgenkey.8 rm -f $(DESTDIR)$(ASTMANDIR)/man8/autosupport.8 rm -f $(DESTDIR)$(ASTMANDIR)/man8/safe_asterisk.8 $(MAKE) -C sounds uninstall uninstall: _uninstall @echo " +--------- Asterisk Uninstall Complete -----+" @echo " + Asterisk binaries, sounds, man pages, +" @echo " + headers, modules, and firmware builds, +" @echo " + have all been uninstalled. +" @echo " + +" @echo " + To remove ALL traces of Asterisk, +" @echo " + including configuration, spool +" @echo " + directories, and logs, run the following +" @echo " + command: +" @echo " + +" ifeq ($(MAKE), gmake) @echo " + $(MAKE) uninstall-all +" else @echo " + $(MAKE) uninstall-all +" endif @echo " +-------------------------------------------+" uninstall-all: _uninstall rm -rf $(DESTDIR)$(ASTLIBDIR) rm -rf $(DESTDIR)$(ASTVARLIBDIR) rm -rf $(DESTDIR)$(ASTDATADIR) rm -rf $(DESTDIR)$(ASTSPOOLDIR) rm -rf $(DESTDIR)$(ASTETCDIR) rm -rf $(DESTDIR)$(ASTLOGDIR) menuconfig: menuselect gmenuconfig: gmenuselect menuselect: menuselect/menuselect menuselect-tree -@menuselect/menuselect menuselect.makeopts $(GLOBAL_MAKEOPTS) $(USER_MAKEOPTS) && (echo "menuselect changes saved!"; rm -f channels/h323/Makefile.ast main/asterisk) || echo "menuselect changes NOT saved!" gmenuselect: menuselect/gmenuselect menuselect-tree -@menuselect/gmenuselect menuselect.makeopts $(GLOBAL_MAKEOPTS) $(USER_MAKEOPTS) && (echo "menuselect changes saved!"; rm -f channels/h323/Makefile.ast main/asterisk) || echo "menuselect changes NOT saved!" menuselect/menuselect: makeopts menuselect/menuselect.c menuselect/menuselect_curses.c menuselect/menuselect_stub.c menuselect/menuselect.h menuselect/linkedlists.h makeopts @CC="$(HOST_CC)" LD="" AR="" RANLIB="" CFLAGS="" $(MAKE) -C menuselect CONFIGURE_SILENT="--silent" menuselect/gmenuselect: makeopts menuselect/menuselect.c menuselect/menuselect_gtk.c menuselect/menuselect_stub.c menuselect/menuselect.h menuselect/linkedlists.h makeopts @CC="$(HOST_CC)" CXX="$(CXX)" LD="" AR="" RANLIB="" CFLAGS="" $(MAKE) -C menuselect _gmenuselect CONFIGURE_SILENT="--silent" menuselect-tree: $(foreach dir,$(filter-out main,$(MOD_SUBDIRS)),$(wildcard $(dir)/*.c) $(wildcard $(dir)/*.cc)) build_tools/cflags.xml build_tools/cflags-devmode.xml sounds/sounds.xml build_tools/embed_modules.xml configure @echo "Generating input for menuselect ..." @echo "" > $@ @echo >> $@ @echo "" >> $@ @for dir in $(sort $(filter-out main,$(MOD_SUBDIRS))); do $(SUBMAKE) -C $${dir} SUBDIR=$${dir} moduleinfo >> $@; done @for dir in $(sort $(filter-out main,$(MOD_SUBDIRS))); do $(SUBMAKE) -C $${dir} SUBDIR=$${dir} makeopts >> $@; done @cat build_tools/cflags.xml >> $@ @if [ "${AST_DEVMODE}" = "yes" ]; then \ cat build_tools/cflags-devmode.xml >> $@; \ fi @cat build_tools/embed_modules.xml >> $@ @cat sounds/sounds.xml >> $@ @echo "" >> $@ .PHONY: menuselect main sounds clean dist-clean distclean all prereqs cleantest uninstall _uninstall uninstall-all dont-optimize $(SUBDIRS_INSTALL) $(SUBDIRS_DIST_CLEAN) $(SUBDIRS_CLEAN) $(SUBDIRS_UNINSTALL) $(SUBDIRS) $(MOD_SUBDIRS_EMBED_LDSCRIPT) $(MOD_SUBDIRS_EMBED_LDFLAGS) $(MOD_SUBDIRS_EMBED_LIBS) badshell menuselect.makeopts include/asterisk/version.h installdirs asterisk-1.4.21.2/pbx/0000755000175000017500000000000011162660652013624 5ustar maniacmaniacasterisk-1.4.21.2/pbx/Makefile0000644000175000017500000000346110765463475015304 0ustar maniacmaniac# # Asterisk -- A telephony toolkit for Linux. # # Makefile for PBX modules # # Copyright (C) 1999-2006, Digium, Inc. # # This program is free software, distributed under the terms of # the GNU General Public License # -include ../menuselect.makeopts ../menuselect.makedeps MENUSELECT_CATEGORY=PBX MENUSELECT_DESCRIPTION=PBX Modules ALL_C_MODS:=$(patsubst %.c,%,$(wildcard pbx_*.c)) ALL_CC_MODS:=$(patsubst %.cc,%,$(wildcard pbx_*.cc)) C_MODS:=$(filter-out $(MENUSELECT_PBX),$(ALL_C_MODS)) CC_MODS:=$(filter-out $(MENUSELECT_PBX),$(ALL_CC_MODS)) LOADABLE_MODS:=$(C_MODS) $(CC_MODS) ifneq ($(findstring pbx,$(MENUSELECT_EMBED)),) EMBEDDED_MODS:=$(LOADABLE_MODS) LOADABLE_MODS:= endif all: _all include $(ASTTOPDIR)/Makefile.moddir_rules clean:: rm -f ael/*.o ael/ael_lex.o: ael/ael_lex.c ../include/asterisk/ael_structs.h ael/ael.tab.h ael/ael_lex.o: ASTCFLAGS+=-I. -Wno-unused ael/ael.tab.o: ael/ael.tab.c ael/ael.tab.h ../include/asterisk/ael_structs.h ael/ael.tab.o: ASTCFLAGS+=-I. ael/ael.tab.o ael/ael_lex.o: ASTCFLAGS+=$(MENUSELECT_OPTS_pbx_ael:%=-D%) $(foreach dep,$(MENUSELECT_DEPENDS_pbx_ael),$(value $(dep)_INCLUDE)) $(if $(filter pbx_ael,$(EMBEDDED_MODS)),modules.link,pbx_ael.so): ael/ael.tab.o ael/ael_lex.o ael/ael_lex.c: (cd ael; flex ael.flex; sed -i -e "/begin standard C headers/i#include \"asterisk.h\"" ael_lex.c) (cd ael; sed 's@#if __STDC_VERSION__ >= 199901L@#if !defined __STDC_VERSION__ || __STDC_VERSION__ >= 199901L@' ael_lex.c > zz; mv zz ael_lex.c) ael/ael.tab.c ael/ael.tab.h: (cd ael; bison -v -d ael.y) dundi-parser.o: dundi-parser.h dundi-parser.o: ASTCFLAGS+=-I. dundi-parser.o: ASTCFLAGS+=$(MENUSELECT_OPTS_pbx_dundi:%=-D%) $(foreach dep,$(MENUSELECT_DEPENDS_pbx_dundi),$(value $(dep)_INCLUDE)) $(if $(filter pbx_dundi,$(EMBEDDED_MODS)),modules.link,pbx_dundi.so): dundi-parser.o asterisk-1.4.21.2/pbx/pbx_gtkconsole.c0000644000175000017500000003276211015063666017022 0ustar maniacmaniac/* * Asterisk -- An open source telephony toolkit. * * Copyright (C) 1999 - 2005, Digium, Inc. * * Mark Spencer * * 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 GTK Console monitor -- very kludgy right now * */ /*** MODULEINFO gtk no ***/ #include "asterisk.h" ASTERISK_FILE_VERSION(__FILE__, "$Revision: 117507 $") #include #include #include #include #include #include #include #include #include #include #include #include "asterisk/pbx.h" #include "asterisk/config.h" #include "asterisk/module.h" #include "asterisk/logger.h" #include "asterisk/options.h" #include "asterisk/cli.h" #include "asterisk/utils.h" #include "asterisk/paths.h" #include "asterisk/term.h" AST_MUTEX_DEFINE_STATIC(verb_lock); static pthread_t console_thread; static int inuse=0; static int clipipe[2]; static int cleanupid = -1; static GtkWidget *window; static GtkWidget *quit; static GtkWidget *closew; static GtkWidget *verb; static GtkWidget *modules; static GtkWidget *statusbar; static GtkWidget *cli; static struct timeval last; static void update_statusbar(char *msg) { gtk_statusbar_pop(GTK_STATUSBAR(statusbar), 1); gtk_statusbar_push(GTK_STATUSBAR(statusbar), 1, msg); } static int unload_module(void) { if (inuse) { /* Kill off the main thread */ pthread_cancel(console_thread); gdk_threads_enter(); gtk_widget_destroy(window); gdk_threads_leave(); close(clipipe[0]); close(clipipe[1]); } return 0; } static int cleanup(void *useless) { gdk_threads_enter(); gtk_clist_thaw(GTK_CLIST(verb)); gtk_widget_queue_resize(verb->parent); gtk_clist_moveto(GTK_CLIST(verb), GTK_CLIST(verb)->rows - 1, 0, 0, 0); cleanupid = -1; gdk_threads_leave(); return 0; } static void __verboser(const char *_stuff) { char *s2[2]; struct timeval tv; int ms; char *stuff; stuff = ast_strdupa(_stuff); term_strip(stuff, stuff, strlen(stuff) + 1); s2[0] = (char *)stuff; s2[1] = NULL; gtk_clist_freeze(GTK_CLIST(verb)); gtk_clist_append(GTK_CLIST(verb), s2); if (!ast_tvzero(last)) { gdk_threads_leave(); gettimeofday(&tv, NULL); if (cleanupid > -1) gtk_timeout_remove(cleanupid); ms = ast_tvdiff_ms(tv, last); if (ms < 100) { /* We just got a message within 100ms, so just schedule an update in the near future */ cleanupid = gtk_timeout_add(200, cleanup, NULL); } else { cleanup(&cleanupid); } last = tv; } else { gettimeofday(&last, NULL); } } static void verboser(const char *stuff) { if (*stuff == 127) { stuff++; } ast_mutex_lock(&verb_lock); /* Lock appropriately if we're really being called in verbose mode */ __verboser(stuff); ast_mutex_unlock(&verb_lock); } static void cliinput(void *data, int source, GdkInputCondition ic) { static char buf[256]; static int offset = 0; int res; char *c; char *l; char n; /* Read as much stuff is there */ res = read(source, buf + offset, sizeof(buf) - 1 - offset); if (res > -1) buf[res + offset] = '\0'; /* make sure we've null terminated whatever we have so far */ c = buf; l = buf; while(*c) { if (*c == '\n') { /* Keep the trailing \n */ c++; n = *c; *c = '\0'; __verboser(l); *(c - 1) = '\0'; *c = n; l = c; } else c++; } if (strlen(l)) { /* We have some left over */ memmove(buf, l, strlen(l) + 1); offset = strlen(buf); } else { offset = 0; } } static void remove_module(void) { int res; char *module; char buf[256]; if (GTK_CLIST(modules)->selection) { module = (char *) gtk_clist_get_row_data(GTK_CLIST(modules), (long) GTK_CLIST(modules)->selection->data); gdk_threads_leave(); res = ast_unload_resource(module, 0); gdk_threads_enter(); if (res) { snprintf(buf, sizeof(buf), "Module '%s' is in use", module); update_statusbar(buf); } else { snprintf(buf, sizeof(buf), "Module '%s' removed", module); update_statusbar(buf); } } } static int reload(void) { int res, x; char *module; char buf[256]; if (GTK_CLIST(modules)->selection) { module= (char *)gtk_clist_get_row_data(GTK_CLIST(modules), (long) GTK_CLIST(modules)->selection->data); module = strdup(module); if (module) { gdk_threads_leave(); res = ast_unload_resource(module, 0); gdk_threads_enter(); if (res) { snprintf(buf, sizeof(buf), "Module '%s' is in use", module); update_statusbar(buf); } else { gdk_threads_leave(); res = ast_load_resource(module); gdk_threads_enter(); if (res) { snprintf(buf, sizeof(buf), "Error reloading module '%s'", module); } else { snprintf(buf, sizeof(buf), "Module '%s' reloaded", module); } for (x=0; x < GTK_CLIST(modules)->rows; x++) { if (!strcmp((char *)gtk_clist_get_row_data(GTK_CLIST(modules), x), module)) { gtk_clist_select_row(GTK_CLIST(modules), x, -1); break; } } update_statusbar(buf); } free(module); } } return 0; } static void file_ok_sel(GtkWidget *w, GtkFileSelection *fs) { char tmp[PATH_MAX]; char *module = gtk_file_selection_get_filename(fs); char buf[256]; snprintf(tmp, sizeof(tmp), "%s/", ast_config_AST_MODULE_DIR); if (!strncmp(module, (char *)tmp, strlen(tmp))) module += strlen(tmp); gdk_threads_leave(); if (ast_load_resource(module)) { snprintf(buf, sizeof(buf), "Error loading module '%s'.", module); update_statusbar(buf); } else { snprintf(buf, sizeof(buf), "Module '%s' loaded", module); update_statusbar(buf); } gdk_threads_enter(); gtk_widget_destroy(GTK_WIDGET(fs)); } static void add_module(void) { char tmp[PATH_MAX]; GtkWidget *filew; snprintf(tmp, sizeof(tmp), "%s/*.so", ast_config_AST_MODULE_DIR); filew = gtk_file_selection_new("Load Module"); gtk_signal_connect(GTK_OBJECT (GTK_FILE_SELECTION(filew)->ok_button), "clicked", GTK_SIGNAL_FUNC(file_ok_sel), filew); gtk_signal_connect_object(GTK_OBJECT (GTK_FILE_SELECTION(filew)->cancel_button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(filew)); gtk_file_selection_set_filename(GTK_FILE_SELECTION(filew), (char *)tmp); gtk_widget_show(filew); } static int add_mod(const char *module, const char *description, int usecount, const char *like) { char use[10]; const char *pass[4]; int row; snprintf(use, sizeof(use), "%d", usecount); pass[0] = module; pass[1] = description; pass[2] = use; pass[3] = NULL; row = gtk_clist_append(GTK_CLIST(modules), (char **) pass); gtk_clist_set_row_data(GTK_CLIST(modules), row, (char *) module); return 0; } static int mod_update(void) { char *module= NULL; /* Update the mod stuff */ if (GTK_CLIST(modules)->selection) { module= (char *)gtk_clist_get_row_data(GTK_CLIST(modules), (long) GTK_CLIST(modules)->selection->data); } gtk_clist_freeze(GTK_CLIST(modules)); gtk_clist_clear(GTK_CLIST(modules)); ast_update_module_list(add_mod, NULL); if (module) gtk_clist_select_row(GTK_CLIST(modules), gtk_clist_find_row_from_data(GTK_CLIST(modules), module), -1); gtk_clist_thaw(GTK_CLIST(modules)); return 1; } static void exit_now(GtkWidget *widget, gpointer data) { ast_loader_unregister(mod_update); gtk_main_quit(); inuse--; ast_update_use_count(); ast_unregister_verbose(verboser); ast_unload_resource("pbx_gtkconsole", 0); if (option_verbose > 1) ast_verbose(VERBOSE_PREFIX_2 "GTK Console Monitor Exiting\n"); /* XXX Trying to quit after calling this makes asterisk segfault XXX */ } static void exit_completely(GtkWidget *widget, gpointer data) { #if 0 /* Clever... */ ast_cli_command(clipipe[1], "quit"); #else kill(getpid(), SIGTERM); #endif } static void exit_nicely(GtkWidget *widget, gpointer data) { fflush(stdout); gtk_widget_destroy(window); } static void *consolethread(void *data) { gtk_widget_show(window); gdk_threads_enter(); gtk_main(); gdk_threads_leave(); return NULL; } static int cli_activate(void) { char buf[256] = ""; strncpy(buf, gtk_entry_get_text(GTK_ENTRY(cli)), sizeof(buf) - 1); gtk_entry_set_text(GTK_ENTRY(cli), ""); if (strlen(buf)) { ast_cli_command(clipipe[1], buf); } return TRUE; } static int show_console(void) { GtkWidget *hbox; GtkWidget *wbox; GtkWidget *notebook; GtkWidget *sw; GtkWidget *bbox, *hbbox, *add, *removew, *reloadw; char *modtitles[3] = { "Module", "Description", "Use Count" }; window = gtk_window_new(GTK_WINDOW_TOPLEVEL); statusbar = gtk_statusbar_new(); gtk_widget_show(statusbar); gtk_signal_connect(GTK_OBJECT(window), "delete_event", GTK_SIGNAL_FUNC (exit_nicely), window); gtk_signal_connect(GTK_OBJECT(window), "destroy", GTK_SIGNAL_FUNC (exit_now), window); gtk_container_set_border_width(GTK_CONTAINER(window), 10); quit = gtk_button_new_with_label("Quit Asterisk"); gtk_signal_connect(GTK_OBJECT(quit), "clicked", GTK_SIGNAL_FUNC (exit_completely), window); gtk_widget_show(quit); closew = gtk_button_new_with_label("Close Window"); gtk_signal_connect(GTK_OBJECT(closew), "clicked", GTK_SIGNAL_FUNC (exit_nicely), window); gtk_widget_show(closew); notebook = gtk_notebook_new(); verb = gtk_clist_new(1); gtk_clist_columns_autosize(GTK_CLIST(verb)); sw = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); gtk_container_add(GTK_CONTAINER(sw), verb); gtk_widget_show(verb); gtk_widget_show(sw); gtk_widget_set_usize(verb, 640, 400); gtk_notebook_append_page(GTK_NOTEBOOK(notebook), sw, gtk_label_new("Verbose Status")); modules = gtk_clist_new_with_titles(3, modtitles); gtk_clist_columns_autosize(GTK_CLIST(modules)); gtk_clist_set_column_auto_resize(GTK_CLIST(modules), 0, TRUE); gtk_clist_set_column_auto_resize(GTK_CLIST(modules), 1, TRUE); gtk_clist_set_column_auto_resize(GTK_CLIST(modules), 2, TRUE); gtk_clist_set_sort_column(GTK_CLIST(modules), 0); gtk_clist_set_auto_sort(GTK_CLIST(modules), TRUE); gtk_clist_column_titles_passive(GTK_CLIST(modules)); sw = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); gtk_container_add(GTK_CONTAINER(sw), modules); gtk_clist_set_selection_mode(GTK_CLIST(modules), GTK_SELECTION_BROWSE); gtk_widget_show(modules); gtk_widget_show(sw); add = gtk_button_new_with_label("Load..."); gtk_widget_show(add); removew = gtk_button_new_with_label("Unload"); gtk_widget_show(removew); reloadw = gtk_button_new_with_label("Reload"); gtk_widget_show(reloadw); gtk_signal_connect(GTK_OBJECT(removew), "clicked", GTK_SIGNAL_FUNC (remove_module), window); gtk_signal_connect(GTK_OBJECT(add), "clicked", GTK_SIGNAL_FUNC (add_module), window); gtk_signal_connect(GTK_OBJECT(reloadw), "clicked", GTK_SIGNAL_FUNC (reload), window); bbox = gtk_vbox_new(FALSE, 5); gtk_widget_show(bbox); gtk_widget_set_usize(bbox, 100, -1); gtk_box_pack_start(GTK_BOX(bbox), add, FALSE, FALSE, 5); gtk_box_pack_start(GTK_BOX(bbox), removew, FALSE, FALSE, 5); gtk_box_pack_start(GTK_BOX(bbox), reloadw, FALSE, FALSE, 5); hbbox = gtk_hbox_new(FALSE, 5); gtk_widget_show(hbbox); gtk_box_pack_start(GTK_BOX(hbbox), sw, TRUE, TRUE, 5); gtk_box_pack_start(GTK_BOX(hbbox), bbox, FALSE, FALSE, 5); gtk_notebook_append_page(GTK_NOTEBOOK(notebook), hbbox, gtk_label_new("Module Information")); gtk_widget_show(notebook); wbox = gtk_hbox_new(FALSE, 5); gtk_widget_show(wbox); gtk_box_pack_end(GTK_BOX(wbox), quit, FALSE, FALSE, 5); gtk_box_pack_end(GTK_BOX(wbox), closew, FALSE, FALSE, 5); hbox = gtk_vbox_new(FALSE, 0); gtk_widget_show(hbox); /* Command line */ cli = gtk_entry_new(); gtk_widget_show(cli); gtk_signal_connect(GTK_OBJECT(cli), "activate", GTK_SIGNAL_FUNC (cli_activate), NULL); gtk_box_pack_start(GTK_BOX(hbox), notebook, TRUE, TRUE, 5); gtk_box_pack_start(GTK_BOX(hbox), wbox, FALSE, FALSE, 5); gtk_box_pack_start(GTK_BOX(hbox), cli, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(hbox), statusbar, FALSE, FALSE, 0); gtk_container_add(GTK_CONTAINER(window), hbox); gtk_window_set_title(GTK_WINDOW(window), "Asterisk Console"); gtk_widget_grab_focus(cli); ast_pthread_create(&console_thread, NULL, consolethread, NULL); /* XXX Okay, seriously fix me! XXX */ usleep(100000); ast_register_verbose(verboser); gtk_clist_freeze(GTK_CLIST(verb)); ast_loader_register(mod_update); gtk_clist_thaw(GTK_CLIST(verb)); gdk_input_add(clipipe[0], GDK_INPUT_READ, cliinput, NULL); mod_update(); update_statusbar("Asterisk Console Ready"); return 0; } static int load_module(void) { if (pipe(clipipe)) { ast_log(LOG_WARNING, "Unable to create CLI pipe\n"); return -1; } g_thread_init(NULL); if (gtk_init_check(NULL, NULL)) { if (!show_console()) { inuse++; ast_update_use_count(); if (option_verbose > 1) ast_verbose( VERBOSE_PREFIX_2 "Launched GTK Console monitor\n"); } else ast_log(LOG_WARNING, "Unable to start GTK console\n"); } else { if (option_debug) ast_log(LOG_DEBUG, "Unable to start GTK console monitor -- ignoring\n"); else if (option_verbose > 1) ast_verbose( VERBOSE_PREFIX_2 "GTK is not available -- skipping monitor\n"); } return 0; } AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "GTK Console", .load = load_module, .unload = unload_module, .reload = reload, ); asterisk-1.4.21.2/pbx/pbx_dundi.c0000644000175000017500000044015310747177331015760 0ustar maniacmaniac/* * Asterisk -- An open source telephony toolkit. * * Copyright (C) 1999 - 2006, Digium, Inc. * * Mark Spencer * * 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 Distributed Universal Number Discovery (DUNDi) */ /*** MODULEINFO zlib ***/ #include "asterisk.h" ASTERISK_FILE_VERSION(__FILE__, "$Revision: 100465 $") #include #include #include #include #include #include #include #include #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(SOLARIS) || defined(__Darwin__) #include #include #endif #include #include #include #include #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__Darwin__) #include #include #endif #include #include #include #include "asterisk/file.h" #include "asterisk/logger.h" #include "asterisk/channel.h" #include "asterisk/config.h" #include "asterisk/options.h" #include "asterisk/pbx.h" #include "asterisk/module.h" #include "asterisk/frame.h" #include "asterisk/file.h" #include "asterisk/cli.h" #include "asterisk/lock.h" #include "asterisk/md5.h" #include "asterisk/dundi.h" #include "asterisk/sched.h" #include "asterisk/io.h" #include "asterisk/utils.h" #include "asterisk/crypto.h" #include "asterisk/astdb.h" #include "asterisk/acl.h" #include "asterisk/aes.h" #include "dundi-parser.h" #define MAX_RESULTS 64 #define MAX_PACKET_SIZE 8192 #define DUNDI_MODEL_INBOUND (1 << 0) #define DUNDI_MODEL_OUTBOUND (1 << 1) #define DUNDI_MODEL_SYMMETRIC (DUNDI_MODEL_INBOUND | DUNDI_MODEL_OUTBOUND) /*! Keep times of last 10 lookups */ #define DUNDI_TIMING_HISTORY 10 #define FLAG_ISREG (1 << 0) /*!< Transaction is register request */ #define FLAG_DEAD (1 << 1) /*!< Transaction is dead */ #define FLAG_FINAL (1 << 2) /*!< Transaction has final message sent */ #define FLAG_ISQUAL (1 << 3) /*!< Transaction is a qualification */ #define FLAG_ENCRYPT (1 << 4) /*!< Transaction is encrypted wiht ECX/DCX */ #define FLAG_SENDFULLKEY (1 << 5) /*!< Send full key on transaction */ #define FLAG_STOREHIST (1 << 6) /*!< Record historic performance */ #define DUNDI_FLAG_INTERNAL_NOPARTIAL (1 << 17) #if 0 #define DUNDI_SECRET_TIME 15 /* Testing only */ #else #define DUNDI_SECRET_TIME DUNDI_DEFAULT_CACHE_TIME #endif #define KEY_OUT 0 #define KEY_IN 1 static struct io_context *io; static struct sched_context *sched; static int netsocket = -1; static pthread_t netthreadid = AST_PTHREADT_NULL; static pthread_t precachethreadid = AST_PTHREADT_NULL; static int tos = 0; static int dundidebug = 0; static int authdebug = 0; static int dundi_ttl = DUNDI_DEFAULT_TTL; static int dundi_key_ttl = DUNDI_DEFAULT_KEY_EXPIRE; static int dundi_cache_time = DUNDI_DEFAULT_CACHE_TIME; static int global_autokilltimeout = 0; static dundi_eid global_eid; static int default_expiration = 60; static int global_storehistory = 0; static char dept[80]; static char org[80]; static char locality[80]; static char stateprov[80]; static char country[80]; static char email[80]; static char phone[80]; static char secretpath[80]; static char cursecret[80]; static char ipaddr[80]; static time_t rotatetime; static dundi_eid empty_eid = { { 0, 0, 0, 0, 0, 0 } }; static int dundi_shutdown = 0; struct permission { AST_LIST_ENTRY(permission) list; int allow; char name[0]; }; struct dundi_packet { AST_LIST_ENTRY(dundi_packet) list; struct dundi_hdr *h; int datalen; struct dundi_transaction *parent; int retransid; int retrans; unsigned char data[0]; }; struct dundi_hint_metadata { unsigned short flags; char exten[AST_MAX_EXTENSION]; }; struct dundi_precache_queue { AST_LIST_ENTRY(dundi_precache_queue) list; char *context; time_t expiration; char number[0]; }; struct dundi_request; struct dundi_transaction { struct sockaddr_in addr; /*!< Other end of transaction */ struct timeval start; /*!< When this transaction was created */ dundi_eid eids[DUNDI_MAX_STACK + 1]; int eidcount; /*!< Number of eids in eids */ dundi_eid us_eid; /*!< Our EID, to them */ dundi_eid them_eid; /*!< Their EID, to us */ aes_encrypt_ctx ecx; /*!< AES 128 Encryption context */ aes_decrypt_ctx dcx; /*!< AES 128 Decryption context */ unsigned int flags; /*!< Has final packet been sent */ int ttl; /*!< Remaining TTL for queries on this one */ int thread; /*!< We have a calling thread */ int retranstimer; /*!< How long to wait before retransmissions */ int autokillid; /*!< ID to kill connection if answer doesn't come back fast enough */ int autokilltimeout; /*!< Recommended timeout for autokill */ unsigned short strans; /*!< Our transaction identifier */ unsigned short dtrans; /*!< Their transaction identifer */ unsigned char iseqno; /*!< Next expected received seqno */ unsigned char oiseqno; /*!< Last received incoming seqno */ unsigned char oseqno; /*!< Next transmitted seqno */ unsigned char aseqno; /*!< Last acknowledge seqno */ AST_LIST_HEAD_NOLOCK(packetlist, dundi_packet) packets; /*!< Packets to be retransmitted */ struct packetlist lasttrans; /*!< Last transmitted / ACK'd packet */ struct dundi_request *parent; /*!< Parent request (if there is one) */ AST_LIST_ENTRY(dundi_transaction) parentlist; /*!< Next with respect to the parent */ AST_LIST_ENTRY(dundi_transaction) all; /*!< Next with respect to all DUNDi transactions */ }; struct dundi_request { char dcontext[AST_MAX_EXTENSION]; char number[AST_MAX_EXTENSION]; dundi_eid query_eid; dundi_eid root_eid; struct dundi_result *dr; struct dundi_entity_info *dei; struct dundi_hint_metadata *hmd; int maxcount; int respcount; int expiration; int cbypass; int pfds[2]; unsigned long crc32; /*!< CRC-32 of all but root EID's in avoid list */ AST_LIST_HEAD_NOLOCK(, dundi_transaction) trans; /*!< Transactions */ AST_LIST_ENTRY(dundi_request) list; }; struct dundi_mapping { char dcontext[AST_MAX_EXTENSION]; char lcontext[AST_MAX_EXTENSION]; int weight; int options; int tech; int dead; char dest[AST_MAX_EXTENSION]; AST_LIST_ENTRY(dundi_mapping) list; }; struct dundi_peer { dundi_eid eid; struct sockaddr_in addr; /*!< Address of DUNDi peer */ AST_LIST_HEAD_NOLOCK(permissionlist, permission) permit; struct permissionlist include; dundi_eid us_eid; char inkey[80]; char outkey[80]; int dead; int registerid; int qualifyid; int sentfullkey; int order; unsigned char txenckey[256]; /*!< Transmitted encrypted key + sig */ unsigned char rxenckey[256]; /*!< Cache received encrypted key + sig */ unsigned long us_keycrc32; /*!< CRC-32 of our key */ aes_encrypt_ctx us_ecx; /*!< Cached AES 128 Encryption context */ aes_decrypt_ctx us_dcx; /*!< Cached AES 128 Decryption context */ unsigned long them_keycrc32; /*!< CRC-32 of our key */ aes_encrypt_ctx them_ecx; /*!< Cached AES 128 Encryption context */ aes_decrypt_ctx them_dcx; /*!< Cached AES 128 Decryption context */ time_t keyexpire; /*!< When to expire/recreate key */ int registerexpire; int lookuptimes[DUNDI_TIMING_HISTORY]; char *lookups[DUNDI_TIMING_HISTORY]; int avgms; struct dundi_transaction *regtrans; /*!< Registration transaction */ struct dundi_transaction *qualtrans; /*!< Qualify transaction */ int model; /*!< Pull model */ int pcmodel; /*!< Push/precache model */ /*! Dynamic peers register with us */ unsigned int dynamic:1; int lastms; /*!< Last measured latency */ int maxms; /*!< Max permissible latency */ struct timeval qualtx; /*!< Time of transmit */ AST_LIST_ENTRY(dundi_peer) list; }; static AST_LIST_HEAD_STATIC(peers, dundi_peer); static AST_LIST_HEAD_STATIC(pcq, dundi_precache_queue); static AST_LIST_HEAD_NOLOCK_STATIC(mappings, dundi_mapping); static AST_LIST_HEAD_NOLOCK_STATIC(requests, dundi_request); static AST_LIST_HEAD_NOLOCK_STATIC(alltrans, dundi_transaction); /*! * \brief Wildcard peer * * This peer is created if the [*] entry is specified in dundi.conf */ static struct dundi_peer *any_peer; static int dundi_xmit(struct dundi_packet *pack); static void dundi_debug_output(const char *data) { if (dundidebug) ast_verbose("%s", data); } static void dundi_error_output(const char *data) { ast_log(LOG_WARNING, "%s", data); } static int has_permission(struct permissionlist *permlist, char *cont) { struct permission *perm; int res = 0; AST_LIST_TRAVERSE(permlist, perm, list) { if (!strcasecmp(perm->name, "all") || !strcasecmp(perm->name, cont)) res = perm->allow; } return res; } static char *tech2str(int tech) { switch(tech) { case DUNDI_PROTO_NONE: return "None"; case DUNDI_PROTO_IAX: return "IAX2"; case DUNDI_PROTO_SIP: return "SIP"; case DUNDI_PROTO_H323: return "H323"; default: return "Unknown"; } } static int str2tech(char *str) { if (!strcasecmp(str, "IAX") || !strcasecmp(str, "IAX2")) return DUNDI_PROTO_IAX; else if (!strcasecmp(str, "SIP")) return DUNDI_PROTO_SIP; else if (!strcasecmp(str, "H323")) return DUNDI_PROTO_H323; else return -1; } static int dundi_lookup_internal(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int ttl, int blockempty, struct dundi_hint_metadata *md, int *expiration, int cybpass, int modeselect, dundi_eid *skip, dundi_eid *avoid[], int direct[]); static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[]); static struct dundi_transaction *create_transaction(struct dundi_peer *p); static struct dundi_transaction *find_transaction(struct dundi_hdr *hdr, struct sockaddr_in *sin) { struct dundi_transaction *trans; /* Look for an exact match first */ AST_LIST_TRAVERSE(&alltrans, trans, all) { if (!inaddrcmp(&trans->addr, sin) && ((trans->strans == (ntohs(hdr->dtrans) & 32767)) /* Matches our destination */ || ((trans->dtrans == (ntohs(hdr->strans) & 32767)) && (!hdr->dtrans))) /* We match their destination */) { if (hdr->strans) trans->dtrans = ntohs(hdr->strans) & 32767; break; } } if (!trans) { switch(hdr->cmdresp & 0x7f) { case DUNDI_COMMAND_DPDISCOVER: case DUNDI_COMMAND_EIDQUERY: case DUNDI_COMMAND_PRECACHERQ: case DUNDI_COMMAND_REGREQ: case DUNDI_COMMAND_NULL: case DUNDI_COMMAND_ENCRYPT: if (hdr->strans) { /* Create new transaction */ trans = create_transaction(NULL); if (trans) { memcpy(&trans->addr, sin, sizeof(trans->addr)); trans->dtrans = ntohs(hdr->strans) & 32767; } else ast_log(LOG_WARNING, "Out of memory!\n"); } break; default: break; } } return trans; } static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied); static int dundi_ack(struct dundi_transaction *trans, int final) { return dundi_send(trans, DUNDI_COMMAND_ACK, 0, final, NULL); } static void dundi_reject(struct dundi_hdr *h, struct sockaddr_in *sin) { struct { struct dundi_packet pack; struct dundi_hdr hdr; } tmp; struct dundi_transaction trans; /* Never respond to an INVALID with another INVALID */ if (h->cmdresp == DUNDI_COMMAND_INVALID) return; memset(&tmp, 0, sizeof(tmp)); memset(&trans, 0, sizeof(trans)); memcpy(&trans.addr, sin, sizeof(trans.addr)); tmp.hdr.strans = h->dtrans; tmp.hdr.dtrans = h->strans; tmp.hdr.iseqno = h->oseqno; tmp.hdr.oseqno = h->iseqno; tmp.hdr.cmdresp = DUNDI_COMMAND_INVALID; tmp.hdr.cmdflags = 0; tmp.pack.h = (struct dundi_hdr *)tmp.pack.data; tmp.pack.datalen = sizeof(struct dundi_hdr); tmp.pack.parent = &trans; dundi_xmit(&tmp.pack); } static void reset_global_eid(void) { #if defined(SIOCGIFHWADDR) int x,s; char eid_str[20]; struct ifreq ifr; s = socket(AF_INET, SOCK_STREAM, 0); if (s > 0) { x = 0; for(x=0;x<10;x++) { memset(&ifr, 0, sizeof(ifr)); snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "eth%d", x); if (!ioctl(s, SIOCGIFHWADDR, &ifr)) { memcpy(&global_eid, ((unsigned char *)&ifr.ifr_hwaddr) + 2, sizeof(global_eid)); ast_log(LOG_DEBUG, "Seeding global EID '%s' from '%s' using 'siocgifhwaddr'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid), ifr.ifr_name); close(s); return; } } close(s); } #else #if defined(ifa_broadaddr) && !defined(SOLARIS) char eid_str[20]; struct ifaddrs *ifap; if (getifaddrs(&ifap) == 0) { struct ifaddrs *p; for (p = ifap; p; p = p->ifa_next) { if ((p->ifa_addr->sa_family == AF_LINK) && !(p->ifa_flags & IFF_LOOPBACK) && (p->ifa_flags & IFF_RUNNING)) { struct sockaddr_dl* sdp = (struct sockaddr_dl*) p->ifa_addr; memcpy(&(global_eid.eid), sdp->sdl_data + sdp->sdl_nlen, 6); ast_log(LOG_DEBUG, "Seeding global EID '%s' from '%s' using 'getifaddrs'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid), p->ifa_name); freeifaddrs(ifap); return; } } freeifaddrs(ifap); } #endif #endif ast_log(LOG_NOTICE, "No ethernet interface found for seeding global EID. You will have to set it manually.\n"); } static int get_trans_id(void) { struct dundi_transaction *t; int stid = (ast_random() % 32766) + 1; int tid = stid; do { AST_LIST_TRAVERSE(&alltrans, t, all) { if (t->strans == tid) break; } if (!t) return tid; tid = (tid % 32766) + 1; } while (tid != stid); return 0; } static int reset_transaction(struct dundi_transaction *trans) { int tid; tid = get_trans_id(); if (tid < 1) return -1; trans->strans = tid; trans->dtrans = 0; trans->iseqno = 0; trans->oiseqno = 0; trans->oseqno = 0; trans->aseqno = 0; ast_clear_flag(trans, FLAG_FINAL); return 0; } static struct dundi_peer *find_peer(dundi_eid *eid) { struct dundi_peer *cur = NULL; if (!eid) eid = &empty_eid; AST_LIST_TRAVERSE(&peers, cur, list) { if (!dundi_eid_cmp(&cur->eid,eid)) break; } if (!cur && any_peer) cur = any_peer; return cur; } static void build_iv(unsigned char *iv) { /* XXX Would be nice to be more random XXX */ unsigned int *fluffy; int x; fluffy = (unsigned int *)(iv); for (x=0;x<4;x++) fluffy[x] = ast_random(); } struct dundi_query_state { dundi_eid *eids[DUNDI_MAX_STACK + 1]; int directs[DUNDI_MAX_STACK + 1]; dundi_eid reqeid; char called_context[AST_MAX_EXTENSION]; char called_number[AST_MAX_EXTENSION]; struct dundi_mapping *maps; int nummaps; int nocache; struct dundi_transaction *trans; void *chal; int challen; int ttl; char fluffy[0]; }; static int dundi_lookup_local(struct dundi_result *dr, struct dundi_mapping *map, char *called_number, dundi_eid *us_eid, int anscnt, struct dundi_hint_metadata *hmd) { struct ast_flags flags = {0}; int x; if (!ast_strlen_zero(map->lcontext)) { if (ast_exists_extension(NULL, map->lcontext, called_number, 1, NULL)) ast_set_flag(&flags, DUNDI_FLAG_EXISTS); if (ast_canmatch_extension(NULL, map->lcontext, called_number, 1, NULL)) ast_set_flag(&flags, DUNDI_FLAG_CANMATCH); if (ast_matchmore_extension(NULL, map->lcontext, called_number, 1, NULL)) ast_set_flag(&flags, DUNDI_FLAG_MATCHMORE); if (ast_ignore_pattern(map->lcontext, called_number)) ast_set_flag(&flags, DUNDI_FLAG_IGNOREPAT); /* Clearly we can't say 'don't ask' anymore if we found anything... */ if (ast_test_flag(&flags, AST_FLAGS_ALL)) ast_clear_flag_nonstd(hmd, DUNDI_HINT_DONT_ASK); if (map->options & DUNDI_FLAG_INTERNAL_NOPARTIAL) { /* Skip partial answers */ ast_clear_flag(&flags, DUNDI_FLAG_MATCHMORE|DUNDI_FLAG_CANMATCH); } if (ast_test_flag(&flags, AST_FLAGS_ALL)) { struct varshead headp; struct ast_var_t *newvariable; ast_set_flag(&flags, map->options & 0xffff); ast_copy_flags(dr + anscnt, &flags, AST_FLAGS_ALL); dr[anscnt].techint = map->tech; dr[anscnt].weight = map->weight; dr[anscnt].expiration = dundi_cache_time; ast_copy_string(dr[anscnt].tech, tech2str(map->tech), sizeof(dr[anscnt].tech)); dr[anscnt].eid = *us_eid; dundi_eid_to_str(dr[anscnt].eid_str, sizeof(dr[anscnt].eid_str), &dr[anscnt].eid); if (ast_test_flag(&flags, DUNDI_FLAG_EXISTS)) { AST_LIST_HEAD_INIT_NOLOCK(&headp); newvariable = ast_var_assign("NUMBER", called_number); AST_LIST_INSERT_HEAD(&headp, newvariable, entries); newvariable = ast_var_assign("EID", dr[anscnt].eid_str); AST_LIST_INSERT_HEAD(&headp, newvariable, entries); newvariable = ast_var_assign("SECRET", cursecret); AST_LIST_INSERT_HEAD(&headp, newvariable, entries); newvariable = ast_var_assign("IPADDR", ipaddr); AST_LIST_INSERT_HEAD(&headp, newvariable, entries); pbx_substitute_variables_varshead(&headp, map->dest, dr[anscnt].dest, sizeof(dr[anscnt].dest)); while ((newvariable = AST_LIST_REMOVE_HEAD(&headp, entries))) ast_var_delete(newvariable); } else dr[anscnt].dest[0] = '\0'; anscnt++; } else { /* No answers... Find the fewest number of digits from the number for which we have no answer. */ char tmp[AST_MAX_EXTENSION + 1] = ""; for (x = 0; x < (sizeof(tmp) - 1); x++) { tmp[x] = called_number[x]; if (!tmp[x]) break; if (!ast_canmatch_extension(NULL, map->lcontext, tmp, 1, NULL)) { /* Oops found something we can't match. If this is longer than the running hint, we have to consider it */ if (strlen(tmp) > strlen(hmd->exten)) { ast_copy_string(hmd->exten, tmp, sizeof(hmd->exten)); } break; } } } } return anscnt; } static void destroy_trans(struct dundi_transaction *trans, int fromtimeout); static void *dundi_lookup_thread(void *data) { struct dundi_query_state *st = data; struct dundi_result dr[MAX_RESULTS]; struct dundi_ie_data ied; struct dundi_hint_metadata hmd; char eid_str[20]; int res, x; int ouranswers=0; int max = 999999; int expiration = dundi_cache_time; ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context, st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves"); memset(&ied, 0, sizeof(ied)); memset(&dr, 0, sizeof(dr)); memset(&hmd, 0, sizeof(hmd)); /* Assume 'don't ask for anything' and 'unaffected', no TTL expired */ hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED; for (x=0;xnummaps;x++) ouranswers = dundi_lookup_local(dr, st->maps + x, st->called_number, &st->trans->us_eid, ouranswers, &hmd); if (ouranswers < 0) ouranswers = 0; for (x=0;xcalled_context, st->called_number, st->ttl, 1, &hmd, &expiration, st->nocache, 0, NULL, st->eids, st->directs); if (res > 0) { /* Append answer in result */ ouranswers += res; } else { if ((res < -1) && (!ouranswers)) dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_DUPLICATE, "Duplicate Request Pending"); } } AST_LIST_LOCK(&peers); /* Truncate if "don't ask" isn't present */ if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK)) hmd.exten[0] = '\0'; if (ast_test_flag(st->trans, FLAG_DEAD)) { ast_log(LOG_DEBUG, "Our transaction went away!\n"); st->trans->thread = 0; destroy_trans(st->trans, 0); } else { for (x=0;x dr[x].expiration)) expiration = dr[x].expiration; dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest); } dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten); dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration); dundi_send(st->trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied); st->trans->thread = 0; } AST_LIST_UNLOCK(&peers); free(st); return NULL; } static void *dundi_precache_thread(void *data) { struct dundi_query_state *st = data; struct dundi_ie_data ied; struct dundi_hint_metadata hmd; char eid_str[20]; ast_log(LOG_DEBUG, "Whee, precaching '%s@%s' for '%s'\n", st->called_number, st->called_context, st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves"); memset(&ied, 0, sizeof(ied)); /* Now produce precache */ dundi_precache_internal(st->called_context, st->called_number, st->ttl, st->eids); AST_LIST_LOCK(&peers); /* Truncate if "don't ask" isn't present */ if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK)) hmd.exten[0] = '\0'; if (ast_test_flag(st->trans, FLAG_DEAD)) { ast_log(LOG_DEBUG, "Our transaction went away!\n"); st->trans->thread = 0; destroy_trans(st->trans, 0); } else { dundi_send(st->trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied); st->trans->thread = 0; } AST_LIST_UNLOCK(&peers); free(st); return NULL; } static int dundi_query_eid_internal(struct dundi_entity_info *dei, const char *dcontext, dundi_eid *eid, struct dundi_hint_metadata *hmd, int ttl, int blockempty, dundi_eid *avoid[]); static void *dundi_query_thread(void *data) { struct dundi_query_state *st = data; struct dundi_entity_info dei; struct dundi_ie_data ied; struct dundi_hint_metadata hmd; char eid_str[20]; int res; ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context, st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves"); memset(&ied, 0, sizeof(ied)); memset(&dei, 0, sizeof(dei)); memset(&hmd, 0, sizeof(hmd)); if (!dundi_eid_cmp(&st->trans->us_eid, &st->reqeid)) { /* Ooh, it's us! */ ast_log(LOG_DEBUG, "Neat, someone look for us!\n"); ast_copy_string(dei.orgunit, dept, sizeof(dei.orgunit)); ast_copy_string(dei.org, org, sizeof(dei.org)); ast_copy_string(dei.locality, locality, sizeof(dei.locality)); ast_copy_string(dei.stateprov, stateprov, sizeof(dei.stateprov)); ast_copy_string(dei.country, country, sizeof(dei.country)); ast_copy_string(dei.email, email, sizeof(dei.email)); ast_copy_string(dei.phone, phone, sizeof(dei.phone)); res = 1; } else { /* If we do not have a canonical result, keep looking */ res = dundi_query_eid_internal(&dei, st->called_context, &st->reqeid, &hmd, st->ttl, 1, st->eids); } AST_LIST_LOCK(&peers); if (ast_test_flag(st->trans, FLAG_DEAD)) { ast_log(LOG_DEBUG, "Our transaction went away!\n"); st->trans->thread = 0; destroy_trans(st->trans, 0); } else { if (res) { dundi_ie_append_str(&ied, DUNDI_IE_DEPARTMENT, dei.orgunit); dundi_ie_append_str(&ied, DUNDI_IE_ORGANIZATION, dei.org); dundi_ie_append_str(&ied, DUNDI_IE_LOCALITY, dei.locality); dundi_ie_append_str(&ied, DUNDI_IE_STATE_PROV, dei.stateprov); dundi_ie_append_str(&ied, DUNDI_IE_COUNTRY, dei.country); dundi_ie_append_str(&ied, DUNDI_IE_EMAIL, dei.email); dundi_ie_append_str(&ied, DUNDI_IE_PHONE, dei.phone); if (!ast_strlen_zero(dei.ipaddr)) dundi_ie_append_str(&ied, DUNDI_IE_IPADDR, dei.ipaddr); } dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten); dundi_send(st->trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied); st->trans->thread = 0; } AST_LIST_UNLOCK(&peers); free(st); return NULL; } static int dundi_answer_entity(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext) { struct dundi_query_state *st; int totallen; int x; int skipfirst=0; struct dundi_ie_data ied; char eid_str[20]; char *s; pthread_t lookupthread; pthread_attr_t attr; if (ies->eidcount > 1) { /* Since it is a requirement that the first EID is the authenticating host and the last EID is the root, it is permissible that the first and last EID could be the same. In that case, we should go ahead copy only the "root" section since we will not need it for authentication. */ if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1])) skipfirst = 1; } totallen = sizeof(struct dundi_query_state); totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid); st = ast_calloc(1, totallen); if (st) { ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context)); memcpy(&st->reqeid, ies->reqeid, sizeof(st->reqeid)); st->trans = trans; st->ttl = ies->ttl - 1; if (st->ttl < 0) st->ttl = 0; s = st->fluffy; for (x=skipfirst;ies->eids[x];x++) { st->eids[x-skipfirst] = (dundi_eid *)s; *st->eids[x-skipfirst] = *ies->eids[x]; s += sizeof(dundi_eid); } ast_log(LOG_DEBUG, "Answering EID query for '%s@%s'!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), ies->reqeid), ies->called_context); pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); trans->thread = 1; if (ast_pthread_create(&lookupthread, &attr, dundi_query_thread, st)) { trans->thread = 0; ast_log(LOG_WARNING, "Unable to create thread!\n"); free(st); memset(&ied, 0, sizeof(ied)); dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads"); dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied); pthread_attr_destroy(&attr); return -1; } pthread_attr_destroy(&attr); } else { ast_log(LOG_WARNING, "Out of memory!\n"); memset(&ied, 0, sizeof(ied)); dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory"); dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied); return -1; } return 0; } static int cache_save_hint(dundi_eid *eidpeer, struct dundi_request *req, struct dundi_hint *hint, int expiration) { int unaffected; char key1[256]; char key2[256]; char eidpeer_str[20]; char eidroot_str[20]; char data[80]; time_t timeout; if (expiration < 0) expiration = dundi_cache_time; /* Only cache hint if "don't ask" is there... */ if (!ast_test_flag_nonstd(hint, htons(DUNDI_HINT_DONT_ASK))) return 0; unaffected = ast_test_flag_nonstd(hint, htons(DUNDI_HINT_UNAFFECTED)); dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer); dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid); snprintf(key1, sizeof(key1), "hint/%s/%s/%s/e%08lx", eidpeer_str, hint->data, req->dcontext, unaffected ? 0 : req->crc32); snprintf(key2, sizeof(key2), "hint/%s/%s/%s/r%s", eidpeer_str, hint->data, req->dcontext, eidroot_str); time(&timeout); timeout += expiration; snprintf(data, sizeof(data), "%ld|", (long)(timeout)); ast_db_put("dundi/cache", key1, data); ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key1); ast_db_put("dundi/cache", key2, data); ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key2); return 0; } static int cache_save(dundi_eid *eidpeer, struct dundi_request *req, int start, int unaffected, int expiration, int push) { int x; char key1[256]; char key2[256]; char data[1024]; char eidpeer_str[20]; char eidroot_str[20]; time_t timeout; if (expiration < 1) expiration = dundi_cache_time; /* Keep pushes a little longer, cut pulls a little short */ if (push) expiration += 10; else expiration -= 10; if (expiration < 1) expiration = 1; dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer); dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid); snprintf(key1, sizeof(key1), "%s/%s/%s/e%08lx", eidpeer_str, req->number, req->dcontext, unaffected ? 0 : req->crc32); snprintf(key2, sizeof(key2), "%s/%s/%s/r%s", eidpeer_str, req->number, req->dcontext, eidroot_str); /* Build request string */ time(&timeout); timeout += expiration; snprintf(data, sizeof(data), "%ld|", (long)(timeout)); for (x=start;xrespcount;x++) { /* Skip anything with an illegal pipe in it */ if (strchr(req->dr[x].dest, '|')) continue; snprintf(data + strlen(data), sizeof(data) - strlen(data), "%d/%d/%d/%s/%s|", req->dr[x].flags, req->dr[x].weight, req->dr[x].techint, req->dr[x].dest, dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), &req->dr[x].eid)); } ast_db_put("dundi/cache", key1, data); ast_db_put("dundi/cache", key2, data); return 0; } static int dundi_prop_precache(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext) { struct dundi_query_state *st; int totallen; int x,z; struct dundi_ie_data ied; char *s; struct dundi_result dr2[MAX_RESULTS]; struct dundi_request dr; struct dundi_hint_metadata hmd; struct dundi_mapping *cur; int mapcount; int skipfirst = 0; pthread_t lookupthread; pthread_attr_t attr; memset(&dr2, 0, sizeof(dr2)); memset(&dr, 0, sizeof(dr)); memset(&hmd, 0, sizeof(hmd)); /* Forge request structure to hold answers for cache */ hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED; dr.dr = dr2; dr.maxcount = MAX_RESULTS; dr.expiration = dundi_cache_time; dr.hmd = &hmd; dr.pfds[0] = dr.pfds[1] = -1; trans->parent = &dr; ast_copy_string(dr.dcontext, ies->called_context ? ies->called_context : "e164", sizeof(dr.dcontext)); ast_copy_string(dr.number, ies->called_number, sizeof(dr.number)); for (x=0;xanscount;x++) { if (trans->parent->respcount < trans->parent->maxcount) { /* Make sure it's not already there */ for (z=0;zparent->respcount;z++) { if ((trans->parent->dr[z].techint == ies->answers[x]->protocol) && !strcmp(trans->parent->dr[z].dest, (char *)ies->answers[x]->data)) break; } if (z == trans->parent->respcount) { /* Copy into parent responses */ trans->parent->dr[trans->parent->respcount].flags = ntohs(ies->answers[x]->flags); trans->parent->dr[trans->parent->respcount].techint = ies->answers[x]->protocol; trans->parent->dr[trans->parent->respcount].weight = ntohs(ies->answers[x]->weight); trans->parent->dr[trans->parent->respcount].eid = ies->answers[x]->eid; if (ies->expiration > 0) trans->parent->dr[trans->parent->respcount].expiration = ies->expiration; else trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time; dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str, sizeof(trans->parent->dr[trans->parent->respcount].eid_str), &ies->answers[x]->eid); ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies->answers[x]->data, sizeof(trans->parent->dr[trans->parent->respcount].dest)); ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies->answers[x]->protocol), sizeof(trans->parent->dr[trans->parent->respcount].tech)); trans->parent->respcount++; ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK); } else if (trans->parent->dr[z].weight > ies->answers[x]->weight) { /* Update weight if appropriate */ trans->parent->dr[z].weight = ies->answers[x]->weight; } } else ast_log(LOG_NOTICE, "Dropping excessive answers in precache for %s@%s\n", trans->parent->number, trans->parent->dcontext); } /* Save all the results (if any) we had. Even if no results, still cache lookup. */ cache_save(&trans->them_eid, trans->parent, 0, 0, ies->expiration, 1); if (ies->hint) cache_save_hint(&trans->them_eid, trans->parent, ies->hint, ies->expiration); totallen = sizeof(struct dundi_query_state); /* Count matching map entries */ mapcount = 0; AST_LIST_TRAVERSE(&mappings, cur, list) { if (!strcasecmp(cur->dcontext, ccontext)) mapcount++; } /* If no maps, return -1 immediately */ if (!mapcount) return -1; if (ies->eidcount > 1) { /* Since it is a requirement that the first EID is the authenticating host and the last EID is the root, it is permissible that the first and last EID could be the same. In that case, we should go ahead copy only the "root" section since we will not need it for authentication. */ if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1])) skipfirst = 1; } /* Prepare to run a query and then propagate that as necessary */ totallen += mapcount * sizeof(struct dundi_mapping); totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid); st = ast_calloc(1, totallen); if (st) { ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context)); ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number)); st->trans = trans; st->ttl = ies->ttl - 1; st->nocache = ies->cbypass; if (st->ttl < 0) st->ttl = 0; s = st->fluffy; for (x=skipfirst;ies->eids[x];x++) { st->eids[x-skipfirst] = (dundi_eid *)s; *st->eids[x-skipfirst] = *ies->eids[x]; st->directs[x-skipfirst] = ies->eid_direct[x]; s += sizeof(dundi_eid); } /* Append mappings */ x = 0; st->maps = (struct dundi_mapping *)s; AST_LIST_TRAVERSE(&mappings, cur, list) { if (!strcasecmp(cur->dcontext, ccontext)) { if (x < mapcount) { st->maps[x] = *cur; st->maps[x].list.next = NULL; x++; } } } st->nummaps = mapcount; ast_log(LOG_DEBUG, "Forwarding precache for '%s@%s'!\n", ies->called_number, ies->called_context); pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); trans->thread = 1; if (ast_pthread_create(&lookupthread, &attr, dundi_precache_thread, st)) { trans->thread = 0; ast_log(LOG_WARNING, "Unable to create thread!\n"); free(st); memset(&ied, 0, sizeof(ied)); dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads"); dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied); pthread_attr_destroy(&attr); return -1; } pthread_attr_destroy(&attr); } else { ast_log(LOG_WARNING, "Out of memory!\n"); memset(&ied, 0, sizeof(ied)); dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory"); dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied); return -1; } return 0; } static int dundi_answer_query(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext) { struct dundi_query_state *st; int totallen; int x; struct dundi_ie_data ied; char *s; struct dundi_mapping *cur; int mapcount = 0; int skipfirst = 0; pthread_t lookupthread; pthread_attr_t attr; totallen = sizeof(struct dundi_query_state); /* Count matching map entries */ AST_LIST_TRAVERSE(&mappings, cur, list) { if (!strcasecmp(cur->dcontext, ccontext)) mapcount++; } /* If no maps, return -1 immediately */ if (!mapcount) return -1; if (ies->eidcount > 1) { /* Since it is a requirement that the first EID is the authenticating host and the last EID is the root, it is permissible that the first and last EID could be the same. In that case, we should go ahead copy only the "root" section since we will not need it for authentication. */ if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1])) skipfirst = 1; } totallen += mapcount * sizeof(struct dundi_mapping); totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid); st = ast_calloc(1, totallen); if (st) { ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context)); ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number)); st->trans = trans; st->ttl = ies->ttl - 1; st->nocache = ies->cbypass; if (st->ttl < 0) st->ttl = 0; s = st->fluffy; for (x=skipfirst;ies->eids[x];x++) { st->eids[x-skipfirst] = (dundi_eid *)s; *st->eids[x-skipfirst] = *ies->eids[x]; st->directs[x-skipfirst] = ies->eid_direct[x]; s += sizeof(dundi_eid); } /* Append mappings */ x = 0; st->maps = (struct dundi_mapping *)s; AST_LIST_TRAVERSE(&mappings, cur, list) { if (!strcasecmp(cur->dcontext, ccontext)) { if (x < mapcount) { st->maps[x] = *cur; st->maps[x].list.next = NULL; x++; } } } st->nummaps = mapcount; ast_log(LOG_DEBUG, "Answering query for '%s@%s'!\n", ies->called_number, ies->called_context); pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); trans->thread = 1; if (ast_pthread_create(&lookupthread, &attr, dundi_lookup_thread, st)) { trans->thread = 0; ast_log(LOG_WARNING, "Unable to create thread!\n"); free(st); memset(&ied, 0, sizeof(ied)); dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads"); dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied); pthread_attr_destroy(&attr); return -1; } pthread_attr_destroy(&attr); } else { ast_log(LOG_WARNING, "Out of memory!\n"); memset(&ied, 0, sizeof(ied)); dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory"); dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied); return -1; } return 0; } static int cache_lookup_internal(time_t now, struct dundi_request *req, char *key, char *eid_str_full, int *lowexpiration) { char data[1024]; char *ptr, *term, *src; int tech; struct ast_flags flags; int weight; int length; int z; char fs[256]; /* Build request string */ if (!ast_db_get("dundi/cache", key, data, sizeof(data))) { time_t timeout; ptr = data; if (!ast_get_time_t(ptr, &timeout, 0, &length)) { int expiration = timeout - now; if (expiration > 0) { ast_log(LOG_DEBUG, "Found cache expiring in %d seconds!\n", expiration); ptr += length + 1; while((sscanf(ptr, "%d/%d/%d/%n", &(flags.flags), &weight, &tech, &length) == 3)) { ptr += length; term = strchr(ptr, '|'); if (term) { *term = '\0'; src = strrchr(ptr, '/'); if (src) { *src = '\0'; src++; } else src = ""; ast_log(LOG_DEBUG, "Found cached answer '%s/%s' originally from '%s' with flags '%s' on behalf of '%s'\n", tech2str(tech), ptr, src, dundi_flags2str(fs, sizeof(fs), flags.flags), eid_str_full); /* Make sure it's not already there */ for (z=0;zrespcount;z++) { if ((req->dr[z].techint == tech) && !strcmp(req->dr[z].dest, ptr)) break; } if (z == req->respcount) { /* Copy into parent responses */ ast_copy_flags(&(req->dr[req->respcount]), &flags, AST_FLAGS_ALL); req->dr[req->respcount].weight = weight; req->dr[req->respcount].techint = tech; req->dr[req->respcount].expiration = expiration; dundi_str_short_to_eid(&req->dr[req->respcount].eid, src); dundi_eid_to_str(req->dr[req->respcount].eid_str, sizeof(req->dr[req->respcount].eid_str), &req->dr[req->respcount].eid); ast_copy_string(req->dr[req->respcount].dest, ptr, sizeof(req->dr[req->respcount].dest)); ast_copy_string(req->dr[req->respcount].tech, tech2str(tech), sizeof(req->dr[req->respcount].tech)); req->respcount++; ast_clear_flag_nonstd(req->hmd, DUNDI_HINT_DONT_ASK); } else if (req->dr[z].weight > weight) req->dr[z].weight = weight; ptr = term + 1; } } /* We found *something* cached */ if (expiration < *lowexpiration) *lowexpiration = expiration; return 1; } else ast_db_del("dundi/cache", key); } else ast_db_del("dundi/cache", key); } return 0; } static int cache_lookup(struct dundi_request *req, dundi_eid *peer_eid, unsigned long crc32, int *lowexpiration) { char key[256]; char eid_str[20]; char eidroot_str[20]; time_t now; int res=0; int res2=0; char eid_str_full[20]; char tmp[256]=""; int x; time(&now); dundi_eid_to_str_short(eid_str, sizeof(eid_str), peer_eid); dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid); dundi_eid_to_str(eid_str_full, sizeof(eid_str_full), peer_eid); snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, crc32); res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration); snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, 0L); res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration); snprintf(key, sizeof(key), "%s/%s/%s/r%s", eid_str, req->number, req->dcontext, eidroot_str); res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration); x = 0; if (!req->respcount) { while(!res2) { /* Look and see if we have a hint that would preclude us from looking at this peer for this number. */ if (!(tmp[x] = req->number[x])) break; x++; /* Check for hints */ snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, crc32); res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration); snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, 0L); res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration); snprintf(key, sizeof(key), "hint/%s/%s/%s/r%s", eid_str, tmp, req->dcontext, eidroot_str); res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration); if (res2) { if (strlen(tmp) > strlen(req->hmd->exten)) { /* Update meta data if appropriate */ ast_copy_string(req->hmd->exten, tmp, sizeof(req->hmd->exten)); } } } res |= res2; } return res; } static void qualify_peer(struct dundi_peer *peer, int schedonly); static void apply_peer(struct dundi_transaction *trans, struct dundi_peer *p) { if (!trans->addr.sin_addr.s_addr) memcpy(&trans->addr, &p->addr, sizeof(trans->addr)); trans->us_eid = p->us_eid; trans->them_eid = p->eid; /* Enable encryption if appropriate */ if (!ast_strlen_zero(p->inkey)) ast_set_flag(trans, FLAG_ENCRYPT); if (p->maxms) { trans->autokilltimeout = p->maxms; trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER; if (p->lastms > 1) { trans->retranstimer = p->lastms * 2; /* Keep it from being silly */ if (trans->retranstimer < 150) trans->retranstimer = 150; } if (trans->retranstimer > DUNDI_DEFAULT_RETRANS_TIMER) trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER; } else trans->autokilltimeout = global_autokilltimeout; } /*! \note Called with the peers list already locked */ static int do_register_expire(const void *data) { struct dundi_peer *peer = (struct dundi_peer *)data; char eid_str[20]; ast_log(LOG_DEBUG, "Register expired for '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid)); peer->registerexpire = -1; peer->lastms = 0; memset(&peer->addr, 0, sizeof(peer->addr)); return 0; } static int update_key(struct dundi_peer *peer) { unsigned char key[16]; struct ast_key *ekey, *skey; char eid_str[20]; int res; if (!peer->keyexpire || (peer->keyexpire < time(NULL))) { build_iv(key); aes_encrypt_key128(key, &peer->us_ecx); aes_decrypt_key128(key, &peer->us_dcx); ekey = ast_key_get(peer->inkey, AST_KEY_PUBLIC); if (!ekey) { ast_log(LOG_NOTICE, "No such key '%s' for creating RSA encrypted shared key for '%s'!\n", peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid)); return -1; } skey = ast_key_get(peer->outkey, AST_KEY_PRIVATE); if (!skey) { ast_log(LOG_NOTICE, "No such key '%s' for signing RSA encrypted shared key for '%s'!\n", peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid)); return -1; } if ((res = ast_encrypt_bin(peer->txenckey, key, sizeof(key), ekey)) != 128) { ast_log(LOG_NOTICE, "Whoa, got a weird encrypt size (%d != %d)!\n", res, 128); return -1; } if ((res = ast_sign_bin(skey, (char *)peer->txenckey, 128, peer->txenckey + 128))) { ast_log(LOG_NOTICE, "Failed to sign key (%d)!\n", res); return -1; } peer->us_keycrc32 = crc32(0L, peer->txenckey, 128); peer->sentfullkey = 0; /* Looks good */ time(&peer->keyexpire); peer->keyexpire += dundi_key_ttl; } return 0; } static int encrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, aes_encrypt_ctx *ecx) { unsigned char curblock[16]; int x; memcpy(curblock, iv, sizeof(curblock)); while(len > 0) { for (x=0;x<16;x++) curblock[x] ^= src[x]; aes_encrypt(curblock, dst, ecx); memcpy(curblock, dst, sizeof(curblock)); dst += 16; src += 16; len -= 16; } return 0; } static int decrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, aes_decrypt_ctx *dcx) { unsigned char lastblock[16]; int x; memcpy(lastblock, iv, sizeof(lastblock)); while(len > 0) { aes_decrypt(src, dst, dcx); for (x=0;x<16;x++) dst[x] ^= lastblock[x]; memcpy(lastblock, src, sizeof(lastblock)); dst += 16; src += 16; len -= 16; } return 0; } static struct dundi_hdr *dundi_decrypt(struct dundi_transaction *trans, unsigned char *dst, int *dstlen, struct dundi_hdr *ohdr, struct dundi_encblock *src, int srclen) { int space = *dstlen; unsigned long bytes; struct dundi_hdr *h; unsigned char *decrypt_space; decrypt_space = alloca(srclen); if (!decrypt_space) return NULL; decrypt_memcpy(decrypt_space, src->encdata, srclen, src->iv, &trans->dcx); /* Setup header */ h = (struct dundi_hdr *)dst; *h = *ohdr; bytes = space - 6; if (uncompress(dst + 6, &bytes, decrypt_space, srclen) != Z_OK) { ast_log(LOG_DEBUG, "Ouch, uncompress failed :(\n"); return NULL; } /* Update length */ *dstlen = bytes + 6; /* Return new header */ return h; } static int dundi_encrypt(struct dundi_transaction *trans, struct dundi_packet *pack) { unsigned char *compress_space; int len; int res; unsigned long bytes; struct dundi_ie_data ied; struct dundi_peer *peer; unsigned char iv[16]; len = pack->datalen + pack->datalen / 100 + 42; compress_space = alloca(len); if (compress_space) { memset(compress_space, 0, len); /* We care about everthing save the first 6 bytes of header */ bytes = len; res = compress(compress_space, &bytes, pack->data + 6, pack->datalen - 6); if (res != Z_OK) { ast_log(LOG_DEBUG, "Ouch, compression failed!\n"); return -1; } memset(&ied, 0, sizeof(ied)); /* Say who we are */ if (!pack->h->iseqno && !pack->h->oseqno) { /* Need the key in the first copy */ if (!(peer = find_peer(&trans->them_eid))) return -1; if (update_key(peer)) return -1; if (!peer->sentfullkey) ast_set_flag(trans, FLAG_SENDFULLKEY); /* Append key data */ dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid); if (ast_test_flag(trans, FLAG_SENDFULLKEY)) { dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128); dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128); } else { dundi_ie_append_int(&ied, DUNDI_IE_KEYCRC32, peer->us_keycrc32); } /* Setup contexts */ trans->ecx = peer->us_ecx; trans->dcx = peer->us_dcx; /* We've sent the full key */ peer->sentfullkey = 1; } /* Build initialization vector */ build_iv(iv); /* Add the field, rounded up to 16 bytes */ dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, iv, NULL, ((bytes + 15) / 16) * 16); /* Copy the data */ if ((ied.pos + bytes) >= sizeof(ied.buf)) { ast_log(LOG_NOTICE, "Final packet too large!\n"); return -1; } encrypt_memcpy(ied.buf + ied.pos, compress_space, bytes, iv, &trans->ecx); ied.pos += ((bytes + 15) / 16) * 16; /* Reconstruct header */ pack->datalen = sizeof(struct dundi_hdr); pack->h->cmdresp = DUNDI_COMMAND_ENCRYPT; pack->h->cmdflags = 0; memcpy(pack->h->ies, ied.buf, ied.pos); pack->datalen += ied.pos; return 0; } return -1; } static int check_key(struct dundi_peer *peer, unsigned char *newkey, unsigned char *newsig, unsigned long keycrc32) { unsigned char dst[128]; int res; struct ast_key *key, *skey; char eid_str[20]; if (option_debug) ast_log(LOG_DEBUG, "Expected '%08lx' got '%08lx'\n", peer->them_keycrc32, keycrc32); if (peer->them_keycrc32 && (peer->them_keycrc32 == keycrc32)) { /* A match */ return 1; } else if (!newkey || !newsig) return 0; if (!memcmp(peer->rxenckey, newkey, 128) && !memcmp(peer->rxenckey + 128, newsig, 128)) { /* By definition, a match */ return 1; } /* Decrypt key */ key = ast_key_get(peer->outkey, AST_KEY_PRIVATE); if (!key) { ast_log(LOG_NOTICE, "Unable to find key '%s' to decode shared key from '%s'\n", peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid)); return -1; } skey = ast_key_get(peer->inkey, AST_KEY_PUBLIC); if (!skey) { ast_log(LOG_NOTICE, "Unable to find key '%s' to verify shared key from '%s'\n", peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid)); return -1; } /* First check signature */ res = ast_check_signature_bin(skey, (char *)newkey, 128, newsig); if (res) return 0; res = ast_decrypt_bin(dst, newkey, sizeof(dst), key); if (res != 16) { if (res >= 0) ast_log(LOG_NOTICE, "Weird, key decoded to the wrong size (%d)\n", res); return 0; } /* Decrypted, passes signature */ ast_log(LOG_DEBUG, "Wow, new key combo passed signature and decrypt!\n"); memcpy(peer->rxenckey, newkey, 128); memcpy(peer->rxenckey + 128, newsig, 128); peer->them_keycrc32 = crc32(0L, peer->rxenckey, 128); aes_decrypt_key128(dst, &peer->them_dcx); aes_encrypt_key128(dst, &peer->them_ecx); return 1; } static void deep_copy_peer(struct dundi_peer *peer_dst, const struct dundi_peer *peer_src) { struct permission *cur, *perm; memcpy(peer_dst, peer_src, sizeof(*peer_dst)); memset(&peer_dst->permit, 0, sizeof(peer_dst->permit)); memset(&peer_dst->include, 0, sizeof(peer_dst->permit)); AST_LIST_TRAVERSE(&peer_src->permit, cur, list) { if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(cur->name) + 1))) continue; perm->allow = cur->allow; strcpy(perm->name, cur->name); AST_LIST_INSERT_HEAD(&peer_dst->permit, perm, list); } AST_LIST_TRAVERSE(&peer_src->include, cur, list) { if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(cur->name) + 1))) continue; perm->allow = cur->allow; strcpy(perm->name, cur->name); AST_LIST_INSERT_HEAD(&peer_dst->include, perm, list); } } static int handle_command_response(struct dundi_transaction *trans, struct dundi_hdr *hdr, int datalen, int encrypted) { /* Handle canonical command / response */ int final = hdr->cmdresp & 0x80; int cmd = hdr->cmdresp & 0x7f; int x,y,z; int resp; int res; int authpass=0; unsigned char *bufcpy; struct dundi_ie_data ied; struct dundi_ies ies; struct dundi_peer *peer = NULL; char eid_str[20]; char eid_str2[20]; memset(&ied, 0, sizeof(ied)); memset(&ies, 0, sizeof(ies)); if (datalen) { bufcpy = alloca(datalen); if (!bufcpy) return -1; /* Make a copy for parsing */ memcpy(bufcpy, hdr->ies, datalen); ast_log(LOG_DEBUG, "Got canonical message %d (%d), %d bytes data%s\n", cmd, hdr->oseqno, datalen, final ? " (Final)" : ""); if (dundi_parse_ies(&ies, bufcpy, datalen) < 0) { ast_log(LOG_WARNING, "Failed to parse DUNDI information elements!\n"); return -1; } } switch(cmd) { case DUNDI_COMMAND_DPDISCOVER: case DUNDI_COMMAND_EIDQUERY: case DUNDI_COMMAND_PRECACHERQ: if (cmd == DUNDI_COMMAND_EIDQUERY) resp = DUNDI_COMMAND_EIDRESPONSE; else if (cmd == DUNDI_COMMAND_PRECACHERQ) resp = DUNDI_COMMAND_PRECACHERP; else resp = DUNDI_COMMAND_DPRESPONSE; /* A dialplan or entity discover -- qualify by highest level entity */ peer = find_peer(ies.eids[0]); if (!peer) { dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL); dundi_send(trans, resp, 0, 1, &ied); } else { int hasauth = 0; trans->us_eid = peer->us_eid; if (strlen(peer->inkey)) { hasauth = encrypted; } else hasauth = 1; if (hasauth) { /* Okay we're authentiated and all, now we check if they're authorized */ if (!ies.called_context) ies.called_context = "e164"; if (cmd == DUNDI_COMMAND_EIDQUERY) { res = dundi_answer_entity(trans, &ies, ies.called_context); } else { if (ast_strlen_zero(ies.called_number)) { /* They're not permitted to access that context */ dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Invalid or missing number/entity"); dundi_send(trans, resp, 0, 1, &ied); } else if ((cmd == DUNDI_COMMAND_DPDISCOVER) && (peer->model & DUNDI_MODEL_INBOUND) && has_permission(&peer->permit, ies.called_context)) { res = dundi_answer_query(trans, &ies, ies.called_context); if (res < 0) { /* There is no such dundi context */ dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context"); dundi_send(trans, resp, 0, 1, &ied); } } else if ((cmd = DUNDI_COMMAND_PRECACHERQ) && (peer->pcmodel & DUNDI_MODEL_INBOUND) && has_permission(&peer->include, ies.called_context)) { res = dundi_prop_precache(trans, &ies, ies.called_context); if (res < 0) { /* There is no such dundi context */ dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context"); dundi_send(trans, resp, 0, 1, &ied); } } else { /* They're not permitted to access that context */ dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Permission to context denied"); dundi_send(trans, resp, 0, 1, &ied); } } } else { /* They're not permitted to access that context */ dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unencrypted responses not permitted"); dundi_send(trans, resp, 0, 1, &ied); } } break; case DUNDI_COMMAND_REGREQ: /* A register request -- should only have one entity */ peer = find_peer(ies.eids[0]); /* if the peer is not found and we have a valid 'any_peer' setting */ if (any_peer && peer == any_peer) { /* copy any_peer into a new peer object */ peer = ast_calloc(1, sizeof(*peer)); if (peer) { deep_copy_peer(peer, any_peer); /* set EID to remote EID */ peer->eid = *ies.eids[0]; AST_LIST_LOCK(&peers); AST_LIST_INSERT_HEAD(&peers, peer, list); AST_LIST_UNLOCK(&peers); } } if (!peer || !peer->dynamic) { dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL); dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied); } else { int hasauth = 0; trans->us_eid = peer->us_eid; if (!ast_strlen_zero(peer->inkey)) { hasauth = encrypted; } else hasauth = 1; if (hasauth) { int expire = default_expiration; char data[256]; int needqual = 0; AST_SCHED_DEL(sched, peer->registerexpire); peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer); snprintf(data, sizeof(data), "%s:%d:%d", ast_inet_ntoa(trans->addr.sin_addr), ntohs(trans->addr.sin_port), expire); ast_db_put("dundi/dpeers", dundi_eid_to_str_short(eid_str, sizeof(eid_str), &peer->eid), data); if (inaddrcmp(&peer->addr, &trans->addr)) { if (option_verbose > 2) { ast_verbose(VERBOSE_PREFIX_3 "Registered DUNDi peer '%s' at '%s:%d'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), ast_inet_ntoa(trans->addr.sin_addr), ntohs(trans->addr.sin_port)); } needqual = 1; } memcpy(&peer->addr, &trans->addr, sizeof(peer->addr)); dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, default_expiration); dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied); if (needqual) qualify_peer(peer, 1); } } break; case DUNDI_COMMAND_DPRESPONSE: /* A dialplan response, lets see what we got... */ if (ies.cause < 1) { /* Success of some sort */ ast_log(LOG_DEBUG, "Looks like success of some sort (%d), %d answers\n", ies.cause, ies.anscount); if (ast_test_flag(trans, FLAG_ENCRYPT)) { authpass = encrypted; } else authpass = 1; if (authpass) { /* Pass back up answers */ if (trans->parent && trans->parent->dr) { y = trans->parent->respcount; for (x=0;xparent->respcount < trans->parent->maxcount) { /* Make sure it's not already there */ for (z=0;zparent->respcount;z++) { if ((trans->parent->dr[z].techint == ies.answers[x]->protocol) && !strcmp(trans->parent->dr[z].dest, (char *)ies.answers[x]->data)) break; } if (z == trans->parent->respcount) { /* Copy into parent responses */ trans->parent->dr[trans->parent->respcount].flags = ntohs(ies.answers[x]->flags); trans->parent->dr[trans->parent->respcount].techint = ies.answers[x]->protocol; trans->parent->dr[trans->parent->respcount].weight = ntohs(ies.answers[x]->weight); trans->parent->dr[trans->parent->respcount].eid = ies.answers[x]->eid; if (ies.expiration > 0) trans->parent->dr[trans->parent->respcount].expiration = ies.expiration; else trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time; dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str, sizeof(trans->parent->dr[trans->parent->respcount].eid_str), &ies.answers[x]->eid); ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies.answers[x]->data, sizeof(trans->parent->dr[trans->parent->respcount].dest)); ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies.answers[x]->protocol), sizeof(trans->parent->dr[trans->parent->respcount].tech)); trans->parent->respcount++; ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK); } else if (trans->parent->dr[z].weight > ies.answers[x]->weight) { /* Update weight if appropriate */ trans->parent->dr[z].weight = ies.answers[x]->weight; } } else ast_log(LOG_NOTICE, "Dropping excessive answers to request for %s@%s\n", trans->parent->number, trans->parent->dcontext); } /* Save all the results (if any) we had. Even if no results, still cache lookup. Let the cache know if this request was unaffected by our entity list. */ cache_save(&trans->them_eid, trans->parent, y, ies.hint ? ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_UNAFFECTED)) : 0, ies.expiration, 0); if (ies.hint) { cache_save_hint(&trans->them_eid, trans->parent, ies.hint, ies.expiration); if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED))) ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED); if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_DONT_ASK))) { if (strlen((char *)ies.hint->data) > strlen(trans->parent->hmd->exten)) { ast_copy_string(trans->parent->hmd->exten, (char *)ies.hint->data, sizeof(trans->parent->hmd->exten)); } } else { ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK); } } if (ies.expiration > 0) { if (trans->parent->expiration > ies.expiration) { trans->parent->expiration = ies.expiration; } } } /* Close connection if not final */ if (!final) dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL); } } else { /* Auth failure, check for data */ if (!final) { /* Cancel if they didn't already */ dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL); } } break; case DUNDI_COMMAND_EIDRESPONSE: /* A dialplan response, lets see what we got... */ if (ies.cause < 1) { /* Success of some sort */ ast_log(LOG_DEBUG, "Looks like success of some sort (%d)\n", ies.cause); if (ast_test_flag(trans, FLAG_ENCRYPT)) { authpass = encrypted; } else authpass = 1; if (authpass) { /* Pass back up answers */ if (trans->parent && trans->parent->dei && ies.q_org) { if (!trans->parent->respcount) { trans->parent->respcount++; if (ies.q_dept) ast_copy_string(trans->parent->dei->orgunit, ies.q_dept, sizeof(trans->parent->dei->orgunit)); if (ies.q_org) ast_copy_string(trans->parent->dei->org, ies.q_org, sizeof(trans->parent->dei->org)); if (ies.q_locality) ast_copy_string(trans->parent->dei->locality, ies.q_locality, sizeof(trans->parent->dei->locality)); if (ies.q_stateprov) ast_copy_string(trans->parent->dei->stateprov, ies.q_stateprov, sizeof(trans->parent->dei->stateprov)); if (ies.q_country) ast_copy_string(trans->parent->dei->country, ies.q_country, sizeof(trans->parent->dei->country)); if (ies.q_email) ast_copy_string(trans->parent->dei->email, ies.q_email, sizeof(trans->parent->dei->email)); if (ies.q_phone) ast_copy_string(trans->parent->dei->phone, ies.q_phone, sizeof(trans->parent->dei->phone)); if (ies.q_ipaddr) ast_copy_string(trans->parent->dei->ipaddr, ies.q_ipaddr, sizeof(trans->parent->dei->ipaddr)); if (!dundi_eid_cmp(&trans->them_eid, &trans->parent->query_eid)) { /* If it's them, update our address */ ast_copy_string(trans->parent->dei->ipaddr, ast_inet_ntoa(trans->addr.sin_addr), sizeof(trans->parent->dei->ipaddr)); } } if (ies.hint) { if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED))) ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED); } } /* Close connection if not final */ if (!final) dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL); } } else { /* Auth failure, check for data */ if (!final) { /* Cancel if they didn't already */ dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL); } } break; case DUNDI_COMMAND_REGRESPONSE: /* A dialplan response, lets see what we got... */ if (ies.cause < 1) { int hasauth; /* Success of some sort */ if (ast_test_flag(trans, FLAG_ENCRYPT)) { hasauth = encrypted; } else hasauth = 1; if (!hasauth) { ast_log(LOG_NOTICE, "Reponse to register not authorized!\n"); if (!final) { dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Improper signature in answer"); dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, &ied); } } else { ast_log(LOG_DEBUG, "Yay, we've registered as '%s' to '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->us_eid), dundi_eid_to_str(eid_str2, sizeof(eid_str2), &trans->them_eid)); /* Close connection if not final */ if (!final) dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL); } } else { /* Auth failure, cancel if they didn't for some reason */ if (!final) { dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL); } } break; case DUNDI_COMMAND_INVALID: case DUNDI_COMMAND_NULL: case DUNDI_COMMAND_PRECACHERP: /* Do nothing special */ if (!final) dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL); break; case DUNDI_COMMAND_ENCREJ: if ((ast_test_flag(trans, FLAG_SENDFULLKEY)) || AST_LIST_EMPTY(&trans->lasttrans) || !(peer = find_peer(&trans->them_eid))) { /* No really, it's over at this point */ if (!final) dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL); } else { /* Send with full key */ ast_set_flag(trans, FLAG_SENDFULLKEY); if (final) { /* Ooops, we got a final message, start by sending ACK... */ dundi_ack(trans, hdr->cmdresp & 0x80); trans->aseqno = trans->iseqno; /* Now, we gotta create a new transaction */ if (!reset_transaction(trans)) { /* Make sure handle_frame doesn't destroy us */ hdr->cmdresp &= 0x7f; /* Parse the message we transmitted */ memset(&ies, 0, sizeof(ies)); dundi_parse_ies(&ies, (AST_LIST_FIRST(&trans->lasttrans))->h->ies, (AST_LIST_FIRST(&trans->lasttrans))->datalen - sizeof(struct dundi_hdr)); /* Reconstruct outgoing encrypted packet */ memset(&ied, 0, sizeof(ied)); dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid); dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128); dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128); if (ies.encblock) dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, ies.encblock->iv, ies.encblock->encdata, ies.enclen); dundi_send(trans, DUNDI_COMMAND_ENCRYPT, 0, (AST_LIST_FIRST(&trans->lasttrans))->h->cmdresp & 0x80, &ied); peer->sentfullkey = 1; } } } break; case DUNDI_COMMAND_ENCRYPT: if (!encrypted) { /* No nested encryption! */ if ((trans->iseqno == 1) && !trans->oseqno) { if (!ies.eids[0] || !(peer = find_peer(ies.eids[0])) || ((!ies.encsharedkey || !ies.encsig) && !ies.keycrc32) || (check_key(peer, ies.encsharedkey, ies.encsig, ies.keycrc32) < 1)) { if (!final) { dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL); } break; } apply_peer(trans, peer); /* Key passed, use new contexts for this session */ trans->ecx = peer->them_ecx; trans->dcx = peer->them_dcx; } if (ast_test_flag(trans, FLAG_ENCRYPT) && ies.encblock && ies.enclen) { struct dundi_hdr *dhdr; unsigned char decoded[MAX_PACKET_SIZE]; int ddatalen; ddatalen = sizeof(decoded); dhdr = dundi_decrypt(trans, decoded, &ddatalen, hdr, ies.encblock, ies.enclen); if (dhdr) { /* Handle decrypted response */ if (dundidebug) dundi_showframe(dhdr, 3, &trans->addr, ddatalen - sizeof(struct dundi_hdr)); handle_command_response(trans, dhdr, ddatalen - sizeof(struct dundi_hdr), 1); /* Carry back final flag */ hdr->cmdresp |= dhdr->cmdresp & 0x80; break; } else ast_log(LOG_DEBUG, "Ouch, decrypt failed :(\n"); } } if (!final) { /* Turn off encryption */ ast_clear_flag(trans, FLAG_ENCRYPT); dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL); } break; default: /* Send unknown command if we don't know it, with final flag IFF it's the first command in the dialog and only if we haven't recieved final notification */ if (!final) { dundi_ie_append_byte(&ied, DUNDI_IE_UNKNOWN, cmd); dundi_send(trans, DUNDI_COMMAND_UNKNOWN, 0, !hdr->oseqno, &ied); } } return 0; } static void destroy_packet(struct dundi_packet *pack, int needfree); static void destroy_packets(struct packetlist *p) { struct dundi_packet *pack; while ((pack = AST_LIST_REMOVE_HEAD(p, list))) { AST_SCHED_DEL(sched, pack->retransid); free(pack); } } static int ack_trans(struct dundi_transaction *trans, int iseqno) { struct dundi_packet *pack; /* Ack transmitted packet corresponding to iseqno */ AST_LIST_TRAVERSE(&trans->packets, pack, list) { if ((pack->h->oseqno + 1) % 255 == iseqno) { destroy_packet(pack, 0); if (!AST_LIST_EMPTY(&trans->lasttrans)) { ast_log(LOG_WARNING, "Whoa, there was still a last trans?\n"); destroy_packets(&trans->lasttrans); } AST_LIST_INSERT_HEAD(&trans->lasttrans, pack, list); AST_SCHED_DEL(sched, trans->autokillid); return 1; } } return 0; } static int handle_frame(struct dundi_hdr *h, struct sockaddr_in *sin, int datalen) { struct dundi_transaction *trans; trans = find_transaction(h, sin); if (!trans) { dundi_reject(h, sin); return 0; } /* Got a transaction, see where this header fits in */ if (h->oseqno == trans->iseqno) { /* Just what we were looking for... Anything but ack increments iseqno */ if (ack_trans(trans, h->iseqno) && ast_test_flag(trans, FLAG_FINAL)) { /* If final, we're done */ destroy_trans(trans, 0); return 0; } if (h->cmdresp != DUNDI_COMMAND_ACK) { trans->oiseqno = trans->iseqno; trans->iseqno++; handle_command_response(trans, h, datalen, 0); } if (trans->aseqno != trans->iseqno) { dundi_ack(trans, h->cmdresp & 0x80); trans->aseqno = trans->iseqno; } /* Delete any saved last transmissions */ destroy_packets(&trans->lasttrans); if (h->cmdresp & 0x80) { /* Final -- destroy now */ destroy_trans(trans, 0); } } else if (h->oseqno == trans->oiseqno) { /* Last incoming sequence number -- send ACK without processing */ dundi_ack(trans, 0); } else { /* Out of window -- simply drop */ ast_log(LOG_DEBUG, "Dropping packet out of window!\n"); } return 0; } static int socket_read(int *id, int fd, short events, void *cbdata) { struct sockaddr_in sin; int res; struct dundi_hdr *h; char buf[MAX_PACKET_SIZE]; socklen_t len; len = sizeof(sin); res = recvfrom(netsocket, buf, sizeof(buf) - 1, 0,(struct sockaddr *) &sin, &len); if (res < 0) { if (errno != ECONNREFUSED) ast_log(LOG_WARNING, "Error: %s\n", strerror(errno)); return 1; } if (res < sizeof(struct dundi_hdr)) { ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, (int)sizeof(struct dundi_hdr)); return 1; } buf[res] = '\0'; h = (struct dundi_hdr *)buf; if (dundidebug) dundi_showframe(h, 1, &sin, res - sizeof(struct dundi_hdr)); AST_LIST_LOCK(&peers); handle_frame(h, &sin, res - sizeof(struct dundi_hdr)); AST_LIST_UNLOCK(&peers); return 1; } static void build_secret(char *secret, int seclen) { unsigned char tmp[16]; char *s; build_iv(tmp); secret[0] = '\0'; ast_base64encode(secret, tmp, sizeof(tmp), seclen); /* Eliminate potential bad characters */ while((s = strchr(secret, ';'))) *s = '+'; while((s = strchr(secret, '/'))) *s = '+'; while((s = strchr(secret, ':'))) *s = '+'; while((s = strchr(secret, '@'))) *s = '+'; } static void save_secret(const char *newkey, const char *oldkey) { char tmp[256]; if (oldkey) snprintf(tmp, sizeof(tmp), "%s;%s", oldkey, newkey); else snprintf(tmp, sizeof(tmp), "%s", newkey); rotatetime = time(NULL) + DUNDI_SECRET_TIME; ast_db_put(secretpath, "secret", tmp); snprintf(tmp, sizeof(tmp), "%d", (int)rotatetime); ast_db_put(secretpath, "secretexpiry", tmp); } static void load_password(void) { char *current=NULL; char *last=NULL; char tmp[256]; time_t expired; ast_db_get(secretpath, "secretexpiry", tmp, sizeof(tmp)); if (!ast_get_time_t(tmp, &expired, 0, NULL)) { ast_db_get(secretpath, "secret", tmp, sizeof(tmp)); current = strchr(tmp, ';'); if (!current) current = tmp; else { *current = '\0'; current++; }; if ((time(NULL) - expired) < 0) { if ((expired - time(NULL)) > DUNDI_SECRET_TIME) expired = time(NULL) + DUNDI_SECRET_TIME; } else if ((time(NULL) - (expired + DUNDI_SECRET_TIME)) < 0) { last = current; current = NULL; } else { last = NULL; current = NULL; } } if (current) { /* Current key is still valid, just setup rotatation properly */ ast_copy_string(cursecret, current, sizeof(cursecret)); rotatetime = expired; } else { /* Current key is out of date, rotate or eliminate all together */ build_secret(cursecret, sizeof(cursecret)); save_secret(cursecret, last); } } static void check_password(void) { char oldsecret[80]; time_t now; time(&now); #if 0 printf("%ld/%ld\n", now, rotatetime); #endif if ((now - rotatetime) >= 0) { /* Time to rotate keys */ ast_copy_string(oldsecret, cursecret, sizeof(oldsecret)); build_secret(cursecret, sizeof(cursecret)); save_secret(cursecret, oldsecret); } } static void *network_thread(void *ignore) { /* Our job is simple: Send queued messages, retrying if necessary. Read frames from the network, and queue them for delivery to the channels */ int res; /* Establish I/O callback for socket read */ ast_io_add(io, netsocket, socket_read, AST_IO_IN, NULL); while (!dundi_shutdown) { res = ast_sched_wait(sched); if ((res > 1000) || (res < 0)) res = 1000; res = ast_io_wait(io, res); if (res >= 0) { AST_LIST_LOCK(&peers); ast_sched_runq(sched); AST_LIST_UNLOCK(&peers); } check_password(); } netthreadid = AST_PTHREADT_NULL; return NULL; } static void *process_precache(void *ign) { struct dundi_precache_queue *qe; time_t now; char context[256]; char number[256]; int run; while (!dundi_shutdown) { time(&now); run = 0; AST_LIST_LOCK(&pcq); if ((qe = AST_LIST_FIRST(&pcq))) { if (!qe->expiration) { /* Gone... Remove... */ AST_LIST_REMOVE_HEAD(&pcq, list); free(qe); } else if (qe->expiration < now) { /* Process this entry */ qe->expiration = 0; ast_copy_string(context, qe->context, sizeof(context)); ast_copy_string(number, qe->number, sizeof(number)); run = 1; } } AST_LIST_UNLOCK(&pcq); if (run) { dundi_precache(context, number); } else sleep(1); } precachethreadid = AST_PTHREADT_NULL; return NULL; } static int start_network_thread(void) { ast_pthread_create_background(&netthreadid, NULL, network_thread, NULL); ast_pthread_create_background(&precachethreadid, NULL, process_precache, NULL); return 0; } static int dundi_do_debug(int fd, int argc, char *argv[]) { if (argc != 2) return RESULT_SHOWUSAGE; dundidebug = 1; ast_cli(fd, "DUNDi Debugging Enabled\n"); return RESULT_SUCCESS; } static int dundi_do_store_history(int fd, int argc, char *argv[]) { if (argc != 3) return RESULT_SHOWUSAGE; global_storehistory = 1; ast_cli(fd, "DUNDi History Storage Enabled\n"); return RESULT_SUCCESS; } static int dundi_flush(int fd, int argc, char *argv[]) { int stats = 0; if ((argc < 2) || (argc > 3)) return RESULT_SHOWUSAGE; if (argc > 2) { if (!strcasecmp(argv[2], "stats")) stats = 1; else return RESULT_SHOWUSAGE; } if (stats) { /* Flush statistics */ struct dundi_peer *p; int x; AST_LIST_LOCK(&peers); AST_LIST_TRAVERSE(&peers, p, list) { for (x = 0;x < DUNDI_TIMING_HISTORY; x++) { if (p->lookups[x]) free(p->lookups[x]); p->lookups[x] = NULL; p->lookuptimes[x] = 0; } p->avgms = 0; } AST_LIST_UNLOCK(&peers); } else { ast_db_deltree("dundi/cache", NULL); ast_cli(fd, "DUNDi Cache Flushed\n"); } return RESULT_SUCCESS; } static int dundi_no_debug(int fd, int argc, char *argv[]) { if (argc != 3) return RESULT_SHOWUSAGE; dundidebug = 0; ast_cli(fd, "DUNDi Debugging Disabled\n"); return RESULT_SUCCESS; } static int dundi_no_store_history(int fd, int argc, char *argv[]) { if (argc != 4) return RESULT_SHOWUSAGE; global_storehistory = 0; ast_cli(fd, "DUNDi History Storage Disabled\n"); return RESULT_SUCCESS; } static char *model2str(int model) { switch(model) { case DUNDI_MODEL_INBOUND: return "Inbound"; case DUNDI_MODEL_OUTBOUND: return "Outbound"; case DUNDI_MODEL_SYMMETRIC: return "Symmetric"; default: return "Unknown"; } } static char *complete_peer_helper(const char *line, const char *word, int pos, int state, int rpos) { int which=0, len; char *ret = NULL; struct dundi_peer *p; char eid_str[20]; if (pos != rpos) return NULL; AST_LIST_LOCK(&peers); len = strlen(word); AST_LIST_TRAVERSE(&peers, p, list) { const char *s = dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid); if (!strncasecmp(word, s, len) && ++which > state) { ret = ast_strdup(s); break; } } AST_LIST_UNLOCK(&peers); return ret; } static char *complete_peer_4(const char *line, const char *word, int pos, int state) { return complete_peer_helper(line, word, pos, state, 3); } static int rescomp(const void *a, const void *b) { const struct dundi_result *resa, *resb; resa = a; resb = b; if (resa->weight < resb->weight) return -1; if (resa->weight > resb->weight) return 1; return 0; } static void sort_results(struct dundi_result *results, int count) { qsort(results, count, sizeof(results[0]), rescomp); } static int dundi_do_lookup(int fd, int argc, char *argv[]) { int res; char tmp[256]; char fs[80] = ""; char *context; int x; int bypass = 0; struct dundi_result dr[MAX_RESULTS]; struct timeval start; if ((argc < 3) || (argc > 4)) return RESULT_SHOWUSAGE; if (argc > 3) { if (!strcasecmp(argv[3], "bypass")) bypass=1; else return RESULT_SHOWUSAGE; } ast_copy_string(tmp, argv[2], sizeof(tmp)); context = strchr(tmp, '@'); if (context) { *context = '\0'; context++; } start = ast_tvnow(); res = dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp, bypass); if (res < 0) ast_cli(fd, "DUNDi lookup returned error.\n"); else if (!res) ast_cli(fd, "DUNDi lookup returned no results.\n"); else sort_results(dr, res); for (x=0;x 3)) return RESULT_SHOWUSAGE; ast_copy_string(tmp, argv[2], sizeof(tmp)); context = strchr(tmp, '@'); if (context) { *context = '\0'; context++; } start = ast_tvnow(); res = dundi_precache(context, tmp); if (res < 0) ast_cli(fd, "DUNDi precache returned error.\n"); else if (!res) ast_cli(fd, "DUNDi precache returned no error.\n"); ast_cli(fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start)); return RESULT_SUCCESS; } static int dundi_do_query(int fd, int argc, char *argv[]) { int res; char tmp[256]; char *context; dundi_eid eid; struct dundi_entity_info dei; if ((argc < 3) || (argc > 3)) return RESULT_SHOWUSAGE; if (dundi_str_to_eid(&eid, argv[2])) { ast_cli(fd, "'%s' is not a valid EID!\n", argv[2]); return RESULT_SHOWUSAGE; } ast_copy_string(tmp, argv[2], sizeof(tmp)); context = strchr(tmp, '@'); if (context) { *context = '\0'; context++; } res = dundi_query_eid(&dei, context, eid); if (res < 0) ast_cli(fd, "DUNDi Query EID returned error.\n"); else if (!res) ast_cli(fd, "DUNDi Query EID returned no results.\n"); else { ast_cli(fd, "DUNDi Query EID succeeded:\n"); ast_cli(fd, "Department: %s\n", dei.orgunit); ast_cli(fd, "Organization: %s\n", dei.org); ast_cli(fd, "City/Locality: %s\n", dei.locality); ast_cli(fd, "State/Province: %s\n", dei.stateprov); ast_cli(fd, "Country: %s\n", dei.country); ast_cli(fd, "E-mail: %s\n", dei.email); ast_cli(fd, "Phone: %s\n", dei.phone); ast_cli(fd, "IP Address: %s\n", dei.ipaddr); } return RESULT_SUCCESS; } static int dundi_show_peer(int fd, int argc, char *argv[]) { struct dundi_peer *peer; struct permission *p; char *order; char eid_str[20]; int x, cnt; if (argc != 4) return RESULT_SHOWUSAGE; AST_LIST_LOCK(&peers); AST_LIST_TRAVERSE(&peers, peer, list) { if (!strcasecmp(dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), argv[3])) break; } if (peer) { switch(peer->order) { case 0: order = "Primary"; break; case 1: order = "Secondary"; break; case 2: order = "Tertiary"; break; case 3: order = "Quartiary"; break; default: order = "Unknown"; } ast_cli(fd, "Peer: %s\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid)); ast_cli(fd, "Model: %s\n", model2str(peer->model)); ast_cli(fd, "Host: %s\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : ""); ast_cli(fd, "Dynamic: %s\n", peer->dynamic ? "yes" : "no"); ast_cli(fd, "Reg: %s\n", peer->registerid < 0 ? "No" : "Yes"); ast_cli(fd, "In Key: %s\n", ast_strlen_zero(peer->inkey) ? "" : peer->inkey); ast_cli(fd, "Out Key: %s\n", ast_strlen_zero(peer->outkey) ? "" : peer->outkey); if (!AST_LIST_EMPTY(&peer->include)) ast_cli(fd, "Include logic%s:\n", peer->model & DUNDI_MODEL_OUTBOUND ? "" : " (IGNORED)"); AST_LIST_TRAVERSE(&peer->include, p, list) ast_cli(fd, "-- %s %s\n", p->allow ? "include" : "do not include", p->name); if (!AST_LIST_EMPTY(&peer->permit)) ast_cli(fd, "Query logic%s:\n", peer->model & DUNDI_MODEL_INBOUND ? "" : " (IGNORED)"); AST_LIST_TRAVERSE(&peer->permit, p, list) ast_cli(fd, "-- %s %s\n", p->allow ? "permit" : "deny", p->name); cnt = 0; for (x = 0;x < DUNDI_TIMING_HISTORY; x++) { if (peer->lookups[x]) { if (!cnt) ast_cli(fd, "Last few query times:\n"); ast_cli(fd, "-- %d. %s (%d ms)\n", x + 1, peer->lookups[x], peer->lookuptimes[x]); cnt++; } } if (cnt) ast_cli(fd, "Average query time: %d ms\n", peer->avgms); } else ast_cli(fd, "No such peer '%s'\n", argv[3]); AST_LIST_UNLOCK(&peers); return RESULT_SUCCESS; } static int dundi_show_peers(int fd, int argc, char *argv[]) { #define FORMAT2 "%-20.20s %-15.15s %-10.10s %-8.8s %-15.15s\n" #define FORMAT "%-20.20s %-15.15s %s %-10.10s %-8.8s %-15.15s\n" struct dundi_peer *peer; int registeredonly=0; char avgms[20]; char eid_str[20]; int online_peers = 0; int offline_peers = 0; int unmonitored_peers = 0; int total_peers = 0; if ((argc != 3) && (argc != 4) && (argc != 5)) return RESULT_SHOWUSAGE; if ((argc == 4)) { if (!strcasecmp(argv[3], "registered")) { registeredonly = 1; } else return RESULT_SHOWUSAGE; } AST_LIST_LOCK(&peers); ast_cli(fd, FORMAT2, "EID", "Host", "Model", "AvgTime", "Status"); AST_LIST_TRAVERSE(&peers, peer, list) { char status[20]; int print_line = -1; char srch[2000]; total_peers++; if (registeredonly && !peer->addr.sin_addr.s_addr) continue; if (peer->maxms) { if (peer->lastms < 0) { strcpy(status, "UNREACHABLE"); offline_peers++; } else if (peer->lastms > peer->maxms) { snprintf(status, sizeof(status), "LAGGED (%d ms)", peer->lastms); offline_peers++; } else if (peer->lastms) { snprintf(status, sizeof(status), "OK (%d ms)", peer->lastms); online_peers++; } else { strcpy(status, "UNKNOWN"); offline_peers++; } } else { strcpy(status, "Unmonitored"); unmonitored_peers++; } if (peer->avgms) snprintf(avgms, sizeof(avgms), "%d ms", peer->avgms); else strcpy(avgms, "Unavail"); snprintf(srch, sizeof(srch), FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)", peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status); if (argc == 5) { if (!strcasecmp(argv[3],"include") && strstr(srch,argv[4])) { print_line = -1; } else if (!strcasecmp(argv[3],"exclude") && !strstr(srch,argv[4])) { print_line = 1; } else if (!strcasecmp(argv[3],"begin") && !strncasecmp(srch,argv[4],strlen(argv[4]))) { print_line = -1; } else { print_line = 0; } } if (print_line) { ast_cli(fd, FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)", peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status); } } ast_cli(fd, "%d dundi peers [%d online, %d offline, %d unmonitored]\n", total_peers, online_peers, offline_peers, unmonitored_peers); AST_LIST_UNLOCK(&peers); return RESULT_SUCCESS; #undef FORMAT #undef FORMAT2 } static int dundi_show_trans(int fd, int argc, char *argv[]) { #define FORMAT2 "%-22.22s %-5.5s %-5.5s %-3.3s %-3.3s %-3.3s\n" #define FORMAT "%-16.16s:%5d %-5.5d %-5.5d %-3.3d %-3.3d %-3.3d\n" struct dundi_transaction *trans; if (argc != 3) return RESULT_SHOWUSAGE; AST_LIST_LOCK(&peers); ast_cli(fd, FORMAT2, "Remote", "Src", "Dst", "Tx", "Rx", "Ack"); AST_LIST_TRAVERSE(&alltrans, trans, all) { ast_cli(fd, FORMAT, ast_inet_ntoa(trans->addr.sin_addr), ntohs(trans->addr.sin_port), trans->strans, trans->dtrans, trans->oseqno, trans->iseqno, trans->aseqno); } AST_LIST_UNLOCK(&peers); return RESULT_SUCCESS; #undef FORMAT #undef FORMAT2 } static int dundi_show_entityid(int fd, int argc, char *argv[]) { char eid_str[20]; if (argc != 3) return RESULT_SHOWUSAGE; AST_LIST_LOCK(&peers); dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid); AST_LIST_UNLOCK(&peers); ast_cli(fd, "Global EID for this system is '%s'\n", eid_str); return RESULT_SUCCESS; } static int dundi_show_requests(int fd, int argc, char *argv[]) { #define FORMAT2 "%-15s %-15s %-15s %-3.3s %-3.3s\n" #define FORMAT "%-15s %-15s %-15s %-3.3d %-3.3d\n" struct dundi_request *req; char eidstr[20]; if (argc != 3) return RESULT_SHOWUSAGE; AST_LIST_LOCK(&peers); ast_cli(fd, FORMAT2, "Number", "Context", "Root", "Max", "Rsp"); AST_LIST_TRAVERSE(&requests, req, list) { ast_cli(fd, FORMAT, req->number, req->dcontext, dundi_eid_zero(&req->root_eid) ? "" : dundi_eid_to_str(eidstr, sizeof(eidstr), &req->root_eid), req->maxcount, req->respcount); } AST_LIST_UNLOCK(&peers); return RESULT_SUCCESS; #undef FORMAT #undef FORMAT2 } /* Grok-a-dial DUNDi */ static int dundi_show_mappings(int fd, int argc, char *argv[]) { #define FORMAT2 "%-12.12s %-7.7s %-12.12s %-10.10s %-5.5s %-25.25s\n" #define FORMAT "%-12.12s %-7d %-12.12s %-10.10s %-5.5s %-25.25s\n" struct dundi_mapping *map; char fs[256]; if (argc != 3) return RESULT_SHOWUSAGE; AST_LIST_LOCK(&peers); ast_cli(fd, FORMAT2, "DUNDi Cntxt", "Weight", "Local Cntxt", "Options", "Tech", "Destination"); AST_LIST_TRAVERSE(&mappings, map, list) { ast_cli(fd, FORMAT, map->dcontext, map->weight, ast_strlen_zero(map->lcontext) ? "" : map->lcontext, dundi_flags2str(fs, sizeof(fs), map->options), tech2str(map->tech), map->dest); } AST_LIST_UNLOCK(&peers); return RESULT_SUCCESS; #undef FORMAT #undef FORMAT2 } static int dundi_show_precache(int fd, int argc, char *argv[]) { #define FORMAT2 "%-12.12s %-12.12s %-10.10s\n" #define FORMAT "%-12.12s %-12.12s %02d:%02d:%02d\n" struct dundi_precache_queue *qe; int h,m,s; time_t now; if (argc != 3) return RESULT_SHOWUSAGE; time(&now); ast_cli(fd, FORMAT2, "Number", "Context", "Expiration"); AST_LIST_LOCK(&pcq); AST_LIST_TRAVERSE(&pcq, qe, list) { s = qe->expiration - now; h = s / 3600; s = s % 3600; m = s / 60; s = s % 60; ast_cli(fd, FORMAT, qe->number, qe->context, h,m,s); } AST_LIST_UNLOCK(&pcq); return RESULT_SUCCESS; #undef FORMAT #undef FORMAT2 } static char debug_usage[] = "Usage: dundi debug\n" " Enables dumping of DUNDi packets for debugging purposes\n"; static char no_debug_usage[] = "Usage: dundi no debug\n" " Disables dumping of DUNDi packets for debugging purposes\n"; static char store_history_usage[] = "Usage: dundi store history\n" " Enables storing of DUNDi requests and times for debugging\n" "purposes\n"; static char no_store_history_usage[] = "Usage: dundi no store history\n" " Disables storing of DUNDi requests and times for debugging\n" "purposes\n"; static char show_peers_usage[] = "Usage: dundi show peers\n" " Lists all known DUNDi peers.\n"; static char show_trans_usage[] = "Usage: dundi show trans\n" " Lists all known DUNDi transactions.\n"; static char show_mappings_usage[] = "Usage: dundi show mappings\n" " Lists all known DUNDi mappings.\n"; static char show_precache_usage[] = "Usage: dundi show precache\n" " Lists all known DUNDi scheduled precache updates.\n"; static char show_entityid_usage[] = "Usage: dundi show entityid\n" " Displays the global entityid for this host.\n"; static char show_peer_usage[] = "Usage: dundi show peer [peer]\n" " Provide a detailed description of a specifid DUNDi peer.\n"; static char show_requests_usage[] = "Usage: dundi show requests\n" " Lists all known pending DUNDi requests.\n"; static char lookup_usage[] = "Usage: dundi lookup [@context] [bypass]\n" " Lookup the given number within the given DUNDi context\n" "(or e164 if none is specified). Bypasses cache if 'bypass'\n" "keyword is specified.\n"; static char precache_usage[] = "Usage: dundi precache [@context]\n" " Lookup the given number within the given DUNDi context\n" "(or e164 if none is specified) and precaches the results to any\n" "upstream DUNDi push servers.\n"; static char query_usage[] = "Usage: dundi query [@context]\n" " Attempts to retrieve contact information for a specific\n" "DUNDi entity identifier (EID) within a given DUNDi context (or\n" "e164 if none is specified).\n"; static char flush_usage[] = "Usage: dundi flush [stats]\n" " Flushes DUNDi answer cache, used primarily for debug. If\n" "'stats' is present, clears timer statistics instead of normal\n" "operation.\n"; static struct ast_cli_entry cli_dundi[] = { { { "dundi", "debug", NULL }, dundi_do_debug, "Enable DUNDi debugging", debug_usage }, { { "dundi", "store", "history", NULL }, dundi_do_store_history, "Enable DUNDi historic records", store_history_usage }, { { "dundi", "no", "store", "history", NULL }, dundi_no_store_history, "Disable DUNDi historic records", no_store_history_usage }, { { "dundi", "flush", NULL }, dundi_flush, "Flush DUNDi cache", flush_usage }, { { "dundi", "no", "debug", NULL }, dundi_no_debug, "Disable DUNDi debugging", no_debug_usage }, { { "dundi", "show", "peers", NULL }, dundi_show_peers, "Show defined DUNDi peers", show_peers_usage }, { { "dundi", "show", "trans", NULL }, dundi_show_trans, "Show active DUNDi transactions", show_trans_usage }, { { "dundi", "show", "entityid", NULL }, dundi_show_entityid, "Display Global Entity ID", show_entityid_usage }, { { "dundi", "show", "mappings", NULL }, dundi_show_mappings, "Show DUNDi mappings", show_mappings_usage }, { { "dundi", "show", "precache", NULL }, dundi_show_precache, "Show DUNDi precache", show_precache_usage }, { { "dundi", "show", "requests", NULL }, dundi_show_requests, "Show DUNDi requests", show_requests_usage }, { { "dundi", "show", "peer", NULL }, dundi_show_peer, "Show info on a specific DUNDi peer", show_peer_usage, complete_peer_4 }, { { "dundi", "lookup", NULL }, dundi_do_lookup, "Lookup a number in DUNDi", lookup_usage }, { { "dundi", "precache", NULL }, dundi_do_precache, "Precache a number in DUNDi", precache_usage }, { { "dundi", "query", NULL }, dundi_do_query, "Query a DUNDi EID", query_usage }, }; static struct dundi_transaction *create_transaction(struct dundi_peer *p) { struct dundi_transaction *trans; int tid; /* Don't allow creation of transactions to non-registered peers */ if (p && !p->addr.sin_addr.s_addr) return NULL; tid = get_trans_id(); if (tid < 1) return NULL; trans = ast_calloc(1, sizeof(*trans)); if (trans) { if (global_storehistory) { trans->start = ast_tvnow(); ast_set_flag(trans, FLAG_STOREHIST); } trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER; trans->autokillid = -1; if (p) { apply_peer(trans, p); if (!p->sentfullkey) ast_set_flag(trans, FLAG_SENDFULLKEY); } trans->strans = tid; AST_LIST_INSERT_HEAD(&alltrans, trans, all); } return trans; } static int dundi_xmit(struct dundi_packet *pack) { int res; if (dundidebug) dundi_showframe(pack->h, 0, &pack->parent->addr, pack->datalen - sizeof(struct dundi_hdr)); res = sendto(netsocket, pack->data, pack->datalen, 0, (struct sockaddr *)&pack->parent->addr, sizeof(pack->parent->addr)); if (res < 0) { ast_log(LOG_WARNING, "Failed to transmit to '%s:%d': %s\n", ast_inet_ntoa(pack->parent->addr.sin_addr), ntohs(pack->parent->addr.sin_port), strerror(errno)); } if (res > 0) res = 0; return res; } static void destroy_packet(struct dundi_packet *pack, int needfree) { if (pack->parent) AST_LIST_REMOVE(&pack->parent->packets, pack, list); AST_SCHED_DEL(sched, pack->retransid); if (needfree) free(pack); } static void destroy_trans(struct dundi_transaction *trans, int fromtimeout) { struct dundi_peer *peer; int ms; int x; int cnt; char eid_str[20]; if (ast_test_flag(trans, FLAG_ISREG | FLAG_ISQUAL | FLAG_STOREHIST)) { AST_LIST_TRAVERSE(&peers, peer, list) { if (peer->regtrans == trans) peer->regtrans = NULL; if (peer->qualtrans == trans) { if (fromtimeout) { if (peer->lastms > -1) ast_log(LOG_NOTICE, "Peer '%s' has become UNREACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid)); peer->lastms = -1; } else { ms = ast_tvdiff_ms(ast_tvnow(), peer->qualtx); if (ms < 1) ms = 1; if (ms < peer->maxms) { if ((peer->lastms >= peer->maxms) || (peer->lastms < 0)) ast_log(LOG_NOTICE, "Peer '%s' has become REACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid)); } else if (peer->lastms < peer->maxms) { ast_log(LOG_NOTICE, "Peer '%s' has become TOO LAGGED (%d ms)\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), ms); } peer->lastms = ms; } peer->qualtrans = NULL; } if (ast_test_flag(trans, FLAG_STOREHIST)) { if (trans->parent && !ast_strlen_zero(trans->parent->number)) { if (!dundi_eid_cmp(&trans->them_eid, &peer->eid)) { peer->avgms = 0; cnt = 0; if (peer->lookups[DUNDI_TIMING_HISTORY-1]) free(peer->lookups[DUNDI_TIMING_HISTORY-1]); for (x=DUNDI_TIMING_HISTORY-1;x>0;x--) { peer->lookuptimes[x] = peer->lookuptimes[x-1]; peer->lookups[x] = peer->lookups[x-1]; if (peer->lookups[x]) { peer->avgms += peer->lookuptimes[x]; cnt++; } } peer->lookuptimes[0] = ast_tvdiff_ms(ast_tvnow(), trans->start); peer->lookups[0] = ast_malloc(strlen(trans->parent->number) + strlen(trans->parent->dcontext) + 2); if (peer->lookups[0]) { sprintf(peer->lookups[0], "%s@%s", trans->parent->number, trans->parent->dcontext); peer->avgms += peer->lookuptimes[0]; cnt++; } if (cnt) peer->avgms /= cnt; } } } } } if (trans->parent) { /* Unlink from parent if appropriate */ AST_LIST_REMOVE(&trans->parent->trans, trans, parentlist); if (AST_LIST_EMPTY(&trans->parent->trans)) { /* Wake up sleeper */ if (trans->parent->pfds[1] > -1) { write(trans->parent->pfds[1], "killa!", 6); } } } /* Unlink from all trans */ AST_LIST_REMOVE(&alltrans, trans, all); destroy_packets(&trans->packets); destroy_packets(&trans->lasttrans); AST_SCHED_DEL(sched, trans->autokillid); if (trans->thread) { /* If used by a thread, mark as dead and be done */ ast_set_flag(trans, FLAG_DEAD); } else free(trans); } static int dundi_rexmit(const void *data) { struct dundi_packet *pack = (struct dundi_packet *)data; int res; AST_LIST_LOCK(&peers); if (pack->retrans < 1) { pack->retransid = -1; if (!ast_test_flag(pack->parent, FLAG_ISQUAL)) ast_log(LOG_NOTICE, "Max retries exceeded to host '%s:%d' msg %d on call %d\n", ast_inet_ntoa(pack->parent->addr.sin_addr), ntohs(pack->parent->addr.sin_port), pack->h->oseqno, ntohs(pack->h->strans)); destroy_trans(pack->parent, 1); res = 0; } else { /* Decrement retransmission, try again */ pack->retrans--; dundi_xmit(pack); res = 1; } AST_LIST_UNLOCK(&peers); return res; } static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied) { struct dundi_packet *pack; int res; int len; char eid_str[20]; len = sizeof(struct dundi_packet) + sizeof(struct dundi_hdr) + (ied ? ied->pos : 0); /* Reserve enough space for encryption */ if (ast_test_flag(trans, FLAG_ENCRYPT)) len += 384; pack = ast_calloc(1, len); if (pack) { pack->h = (struct dundi_hdr *)(pack->data); if (cmdresp != DUNDI_COMMAND_ACK) { pack->retransid = ast_sched_add(sched, trans->retranstimer, dundi_rexmit, pack); pack->retrans = DUNDI_DEFAULT_RETRANS - 1; AST_LIST_INSERT_HEAD(&trans->packets, pack, list); } pack->parent = trans; pack->h->strans = htons(trans->strans); pack->h->dtrans = htons(trans->dtrans); pack->h->iseqno = trans->iseqno; pack->h->oseqno = trans->oseqno; pack->h->cmdresp = cmdresp; pack->datalen = sizeof(struct dundi_hdr); if (ied) { memcpy(pack->h->ies, ied->buf, ied->pos); pack->datalen += ied->pos; } if (final) { pack->h->cmdresp |= DUNDI_COMMAND_FINAL; ast_set_flag(trans, FLAG_FINAL); } pack->h->cmdflags = flags; if (cmdresp != DUNDI_COMMAND_ACK) { trans->oseqno++; trans->oseqno = trans->oseqno % 256; } trans->aseqno = trans->iseqno; /* If we have their public key, encrypt */ if (ast_test_flag(trans, FLAG_ENCRYPT)) { switch(cmdresp) { case DUNDI_COMMAND_REGREQ: case DUNDI_COMMAND_REGRESPONSE: case DUNDI_COMMAND_DPDISCOVER: case DUNDI_COMMAND_DPRESPONSE: case DUNDI_COMMAND_EIDQUERY: case DUNDI_COMMAND_EIDRESPONSE: case DUNDI_COMMAND_PRECACHERQ: case DUNDI_COMMAND_PRECACHERP: if (dundidebug) dundi_showframe(pack->h, 2, &trans->addr, pack->datalen - sizeof(struct dundi_hdr)); res = dundi_encrypt(trans, pack); break; default: res = 0; } } else res = 0; if (!res) res = dundi_xmit(pack); if (res) ast_log(LOG_NOTICE, "Failed to send packet to '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid)); if (cmdresp == DUNDI_COMMAND_ACK) free(pack); return res; } return -1; } static int do_autokill(const void *data) { struct dundi_transaction *trans = (struct dundi_transaction *)data; char eid_str[20]; ast_log(LOG_NOTICE, "Transaction to '%s' took too long to ACK, destroying\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid)); trans->autokillid = -1; destroy_trans(trans, 0); /* We could actually set it to 1 instead of 0, but we won't ;-) */ return 0; } static void dundi_ie_append_eid_appropriately(struct dundi_ie_data *ied, char *context, dundi_eid *eid, dundi_eid *us) { struct dundi_peer *p; if (!dundi_eid_cmp(eid, us)) { dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid); return; } AST_LIST_LOCK(&peers); AST_LIST_TRAVERSE(&peers, p, list) { if (!dundi_eid_cmp(&p->eid, eid)) { if (has_permission(&p->include, context)) dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid); else dundi_ie_append_eid(ied, DUNDI_IE_EID, eid); break; } } if (!p) dundi_ie_append_eid(ied, DUNDI_IE_EID, eid); AST_LIST_UNLOCK(&peers); } static int dundi_discover(struct dundi_transaction *trans) { struct dundi_ie_data ied; int x; if (!trans->parent) { ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n"); return -1; } memset(&ied, 0, sizeof(ied)); dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION); if (!dundi_eid_zero(&trans->us_eid)) dundi_ie_append_eid(&ied, DUNDI_IE_EID_DIRECT, &trans->us_eid); for (x=0;xeidcount;x++) dundi_ie_append_eid_appropriately(&ied, trans->parent->dcontext, &trans->eids[x], &trans->us_eid); dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number); dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext); dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl); if (trans->parent->cbypass) dundi_ie_append(&ied, DUNDI_IE_CACHEBYPASS); if (trans->autokilltimeout) trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans); return dundi_send(trans, DUNDI_COMMAND_DPDISCOVER, 0, 0, &ied); } static int precache_trans(struct dundi_transaction *trans, struct dundi_mapping *maps, int mapcount, int *minexp, int *foundanswers) { struct dundi_ie_data ied; int x, res; int max = 999999; int expiration = dundi_cache_time; int ouranswers=0; dundi_eid *avoid[1] = { NULL, }; int direct[1] = { 0, }; struct dundi_result dr[MAX_RESULTS]; struct dundi_hint_metadata hmd; if (!trans->parent) { ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n"); return -1; } memset(&hmd, 0, sizeof(hmd)); memset(&dr, 0, sizeof(dr)); /* Look up the answers we're going to include */ for (x=0;xparent->number, &trans->us_eid, ouranswers, &hmd); if (ouranswers < 0) ouranswers = 0; for (x=0;xparent->dcontext, trans->parent->number, trans->ttl, 1, &hmd, &expiration, 0, 1, &trans->them_eid, avoid, direct); if (res > 0) { /* Append answer in result */ ouranswers += res; } } if (ouranswers > 0) { *foundanswers += ouranswers; memset(&ied, 0, sizeof(ied)); dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION); if (!dundi_eid_zero(&trans->us_eid)) dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid); for (x=0;xeidcount;x++) dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]); dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number); dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext); dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl); for (x=0;x dr[x].expiration)) expiration = dr[x].expiration; dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest); } dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten); dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration); if (trans->autokilltimeout) trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans); if (expiration < *minexp) *minexp = expiration; return dundi_send(trans, DUNDI_COMMAND_PRECACHERQ, 0, 0, &ied); } else { /* Oops, nothing to send... */ destroy_trans(trans, 0); return 0; } } static int dundi_query(struct dundi_transaction *trans) { struct dundi_ie_data ied; int x; if (!trans->parent) { ast_log(LOG_WARNING, "Tried to query a transaction with no parent?!?\n"); return -1; } memset(&ied, 0, sizeof(ied)); dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION); if (!dundi_eid_zero(&trans->us_eid)) dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid); for (x=0;xeidcount;x++) dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]); dundi_ie_append_eid(&ied, DUNDI_IE_REQEID, &trans->parent->query_eid); dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext); dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl); if (trans->autokilltimeout) trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans); return dundi_send(trans, DUNDI_COMMAND_EIDQUERY, 0, 0, &ied); } static int discover_transactions(struct dundi_request *dr) { struct dundi_transaction *trans; AST_LIST_LOCK(&peers); AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) { dundi_discover(trans); } AST_LIST_UNLOCK(&peers); return 0; } static int precache_transactions(struct dundi_request *dr, struct dundi_mapping *maps, int mapcount, int *expiration, int *foundanswers) { struct dundi_transaction *trans; /* Mark all as "in thread" so they don't disappear */ AST_LIST_LOCK(&peers); AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) { if (trans->thread) ast_log(LOG_WARNING, "This shouldn't happen, really...\n"); trans->thread = 1; } AST_LIST_UNLOCK(&peers); AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) { if (!ast_test_flag(trans, FLAG_DEAD)) precache_trans(trans, maps, mapcount, expiration, foundanswers); } /* Cleanup any that got destroyed in the mean time */ AST_LIST_LOCK(&peers); AST_LIST_TRAVERSE_SAFE_BEGIN(&dr->trans, trans, parentlist) { trans->thread = 0; if (ast_test_flag(trans, FLAG_DEAD)) { ast_log(LOG_DEBUG, "Our transaction went away!\n"); /* This is going to remove the transaction from the dundi_request's list, as well * as the global transactions list */ destroy_trans(trans, 0); } } AST_LIST_TRAVERSE_SAFE_END AST_LIST_UNLOCK(&peers); return 0; } static int query_transactions(struct dundi_request *dr) { struct dundi_transaction *trans; AST_LIST_LOCK(&peers); AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) { dundi_query(trans); } AST_LIST_UNLOCK(&peers); return 0; } static int optimize_transactions(struct dundi_request *dr, int order) { /* Minimize the message propagation through DUNDi by alerting the network to hops which should be not be considered */ struct dundi_transaction *trans; struct dundi_peer *peer; dundi_eid tmp; int x; int needpush; AST_LIST_LOCK(&peers); AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) { /* Pop off the true root */ if (trans->eidcount) { tmp = trans->eids[--trans->eidcount]; needpush = 1; } else { tmp = trans->us_eid; needpush = 0; } AST_LIST_TRAVERSE(&peers, peer, list) { if (has_permission(&peer->include, dr->dcontext) && dundi_eid_cmp(&peer->eid, &trans->them_eid) && (peer->order <= order)) { /* For each other transaction, make sure we don't ask this EID about the others if they're not already in the list */ if (!dundi_eid_cmp(&tmp, &peer->eid)) x = -1; else { for (x=0;xeidcount;x++) { if (!dundi_eid_cmp(&trans->eids[x], &peer->eid)) break; } } if (x == trans->eidcount) { /* Nope not in the list, if needed, add us at the end since we're the source */ if (trans->eidcount < DUNDI_MAX_STACK - needpush) { trans->eids[trans->eidcount++] = peer->eid; /* Need to insert the real root (or us) at the bottom now as a requirement now. */ needpush = 1; } } } } /* If necessary, push the true root back on the end */ if (needpush) trans->eids[trans->eidcount++] = tmp; } AST_LIST_UNLOCK(&peers); return 0; } static int append_transaction(struct dundi_request *dr, struct dundi_peer *p, int ttl, dundi_eid *avoid[]) { struct dundi_transaction *trans; int x; char eid_str[20]; char eid_str2[20]; /* Ignore if not registered */ if (!p->addr.sin_addr.s_addr) return 0; if (p->maxms && ((p->lastms < 0) || (p->lastms >= p->maxms))) return 0; if (ast_strlen_zero(dr->number)) ast_log(LOG_DEBUG, "Will query peer '%s' for '%s' (context '%s')\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid), dundi_eid_to_str(eid_str2, sizeof(eid_str2), &dr->query_eid), dr->dcontext); else ast_log(LOG_DEBUG, "Will query peer '%s' for '%s@%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid), dr->number, dr->dcontext); trans = create_transaction(p); if (!trans) return -1; trans->parent = dr; trans->ttl = ttl; for (x = 0; avoid[x] && (x < DUNDI_MAX_STACK); x++) trans->eids[x] = *avoid[x]; trans->eidcount = x; AST_LIST_INSERT_HEAD(&dr->trans, trans, parentlist); return 0; } static void cancel_request(struct dundi_request *dr) { struct dundi_transaction *trans; AST_LIST_LOCK(&peers); while ((trans = AST_LIST_REMOVE_HEAD(&dr->trans, parentlist))) { /* Orphan transaction from request */ trans->parent = NULL; /* Send final cancel */ dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL); } AST_LIST_UNLOCK(&peers); } static void abort_request(struct dundi_request *dr) { struct dundi_transaction *trans; AST_LIST_LOCK(&peers); while ((trans = AST_LIST_FIRST(&dr->trans))) { /* This will remove the transaction from the list */ destroy_trans(trans, 0); } AST_LIST_UNLOCK(&peers); } static void build_transactions(struct dundi_request *dr, int ttl, int order, int *foundcache, int *skipped, int blockempty, int nocache, int modeselect, dundi_eid *skip, dundi_eid *avoid[], int directs[]) { struct dundi_peer *p; int x; int res; int pass; int allowconnect; char eid_str[20]; AST_LIST_LOCK(&peers); AST_LIST_TRAVERSE(&peers, p, list) { if (modeselect == 1) { /* Send the precache to push upstreams only! */ pass = has_permission(&p->permit, dr->dcontext) && (p->pcmodel & DUNDI_MODEL_OUTBOUND); allowconnect = 1; } else { /* Normal lookup / EID query */ pass = has_permission(&p->include, dr->dcontext); allowconnect = p->model & DUNDI_MODEL_OUTBOUND; } if (skip) { if (!dundi_eid_cmp(skip, &p->eid)) pass = 0; } if (pass) { if (p->order <= order) { /* Check order first, then check cache, regardless of omissions, this gets us more likely to not have an affected answer. */ if((nocache || !(res = cache_lookup(dr, &p->eid, dr->crc32, &dr->expiration)))) { res = 0; /* Make sure we haven't already seen it and that it won't affect our answer */ for (x=0;avoid[x];x++) { if (!dundi_eid_cmp(avoid[x], &p->eid) || !dundi_eid_cmp(avoid[x], &p->us_eid)) { /* If not a direct connection, it affects our answer */ if (directs && !directs[x]) ast_clear_flag_nonstd(dr->hmd, DUNDI_HINT_UNAFFECTED); break; } } /* Make sure we can ask */ if (allowconnect) { if (!avoid[x] && (!blockempty || !dundi_eid_zero(&p->us_eid))) { /* Check for a matching or 0 cache entry */ append_transaction(dr, p, ttl, avoid); } else ast_log(LOG_DEBUG, "Avoiding '%s' in transaction\n", dundi_eid_to_str(eid_str, sizeof(eid_str), avoid[x])); } } *foundcache |= res; } else if (!*skipped || (p->order < *skipped)) *skipped = p->order; } } AST_LIST_UNLOCK(&peers); } static int register_request(struct dundi_request *dr, struct dundi_request **pending) { struct dundi_request *cur; int res=0; char eid_str[20]; AST_LIST_LOCK(&peers); AST_LIST_TRAVERSE(&requests, cur, list) { if (option_debug) ast_log(LOG_DEBUG, "Checking '%s@%s' vs '%s@%s'\n", cur->dcontext, cur->number, dr->dcontext, dr->number); if (!strcasecmp(cur->dcontext, dr->dcontext) && !strcasecmp(cur->number, dr->number) && (!dundi_eid_cmp(&cur->root_eid, &dr->root_eid) || (cur->crc32 == dr->crc32))) { ast_log(LOG_DEBUG, "Found existing query for '%s@%s' for '%s' crc '%08lx'\n", cur->dcontext, cur->number, dundi_eid_to_str(eid_str, sizeof(eid_str), &cur->root_eid), cur->crc32); *pending = cur; res = 1; break; } } if (!res) { ast_log(LOG_DEBUG, "Registering request for '%s@%s' on behalf of '%s' crc '%08lx'\n", dr->number, dr->dcontext, dundi_eid_to_str(eid_str, sizeof(eid_str), &dr->root_eid), dr->crc32); /* Go ahead and link us in since nobody else is searching for this */ AST_LIST_INSERT_HEAD(&requests, dr, list); *pending = NULL; } AST_LIST_UNLOCK(&peers); return res; } static void unregister_request(struct dundi_request *dr) { AST_LIST_LOCK(&peers); AST_LIST_REMOVE(&requests, dr, list); AST_LIST_UNLOCK(&peers); } static int check_request(struct dundi_request *dr) { struct dundi_request *cur; AST_LIST_LOCK(&peers); AST_LIST_TRAVERSE(&requests, cur, list) { if (cur == dr) break; } AST_LIST_UNLOCK(&peers); return cur ? 1 : 0; } static unsigned long avoid_crc32(dundi_eid *avoid[]) { /* Idea is that we're calculating a checksum which is independent of the order that the EID's are listed in */ unsigned long acrc32 = 0; int x; for (x=0;avoid[x];x++) { /* Order doesn't matter */ if (avoid[x+1]) { acrc32 ^= crc32(0L, (unsigned char *)avoid[x], sizeof(dundi_eid)); } } return acrc32; } static int dundi_lookup_internal(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int ttl, int blockempty, struct dundi_hint_metadata *hmd, int *expiration, int cbypass, int modeselect, dundi_eid *skip, dundi_eid *avoid[], int direct[]) { int res; struct dundi_request dr, *pending; dundi_eid *rooteid=NULL; int x; int ttlms; int ms; int foundcache; int skipped=0; int order=0; char eid_str[20]; struct timeval start; /* Don't do anthing for a hungup channel */ if (chan && chan->_softhangup) return 0; ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME; for (x=0;avoid[x];x++) rooteid = avoid[x]; /* Now perform real check */ memset(&dr, 0, sizeof(dr)); if (pipe(dr.pfds)) { ast_log(LOG_WARNING, "pipe failed: %s\n" , strerror(errno)); return -1; } dr.dr = result; dr.hmd = hmd; dr.maxcount = maxret; dr.expiration = *expiration; dr.cbypass = cbypass; dr.crc32 = avoid_crc32(avoid); ast_copy_string(dr.dcontext, dcontext ? dcontext : "e164", sizeof(dr.dcontext)); ast_copy_string(dr.number, number, sizeof(dr.number)); if (rooteid) dr.root_eid = *rooteid; res = register_request(&dr, &pending); if (res) { /* Already a request */ if (rooteid && !dundi_eid_cmp(&dr.root_eid, &pending->root_eid)) { /* This is on behalf of someone else. Go ahead and close this out since they'll get their answer anyway. */ ast_log(LOG_DEBUG, "Oooh, duplicate request for '%s@%s' for '%s'\n", dr.number,dr.dcontext,dundi_eid_to_str(eid_str, sizeof(eid_str), &dr.root_eid)); close(dr.pfds[0]); close(dr.pfds[1]); return -2; } else { /* Wait for the cache to populate */ ast_log(LOG_DEBUG, "Waiting for similar request for '%s@%s' for '%s'\n", dr.number,dr.dcontext,dundi_eid_to_str(eid_str, sizeof(eid_str), &pending->root_eid)); start = ast_tvnow(); while(check_request(pending) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms) && (!chan || !chan->_softhangup)) { /* XXX Would be nice to have a way to poll/select here XXX */ /* XXX this is a busy wait loop!!! */ usleep(1); } /* Continue on as normal, our cache should kick in */ } } /* Create transactions */ do { order = skipped; skipped = 0; foundcache = 0; build_transactions(&dr, ttl, order, &foundcache, &skipped, blockempty, cbypass, modeselect, skip, avoid, direct); } while (skipped && !foundcache && AST_LIST_EMPTY(&dr.trans)); /* If no TTL, abort and return 0 now after setting TTL expired hint. Couldn't do this earlier because we didn't know if we were going to have transactions or not. */ if (!ttl) { ast_set_flag_nonstd(hmd, DUNDI_HINT_TTL_EXPIRED); abort_request(&dr); unregister_request(&dr); close(dr.pfds[0]); close(dr.pfds[1]); return 0; } /* Optimize transactions */ optimize_transactions(&dr, order); /* Actually perform transactions */ discover_transactions(&dr); /* Wait for transaction to come back */ start = ast_tvnow(); while (!AST_LIST_EMPTY(&dr.trans) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms) && (!chan || !chan->_softhangup)) { ms = 100; ast_waitfor_n_fd(dr.pfds, 1, &ms, NULL); } if (chan && chan->_softhangup) ast_log(LOG_DEBUG, "Hrm, '%s' hungup before their query for %s@%s finished\n", chan->name, dr.number, dr.dcontext); cancel_request(&dr); unregister_request(&dr); res = dr.respcount; *expiration = dr.expiration; close(dr.pfds[0]); close(dr.pfds[1]); return res; } int dundi_lookup(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int cbypass) { struct dundi_hint_metadata hmd; dundi_eid *avoid[1] = { NULL, }; int direct[1] = { 0, }; int expiration = dundi_cache_time; memset(&hmd, 0, sizeof(hmd)); hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED; return dundi_lookup_internal(result, maxret, chan, dcontext, number, dundi_ttl, 0, &hmd, &expiration, cbypass, 0, NULL, avoid, direct); } static void reschedule_precache(const char *number, const char *context, int expiration) { int len; struct dundi_precache_queue *qe, *prev; AST_LIST_LOCK(&pcq); AST_LIST_TRAVERSE_SAFE_BEGIN(&pcq, qe, list) { if (!strcmp(number, qe->number) && !strcasecmp(context, qe->context)) { AST_LIST_REMOVE_CURRENT(&pcq, list); break; } } AST_LIST_TRAVERSE_SAFE_END if (!qe) { len = sizeof(*qe); len += strlen(number) + 1; len += strlen(context) + 1; if (!(qe = ast_calloc(1, len))) { AST_LIST_UNLOCK(&pcq); return; } strcpy(qe->number, number); qe->context = qe->number + strlen(number) + 1; strcpy(qe->context, context); } time(&qe->expiration); qe->expiration += expiration; if ((prev = AST_LIST_FIRST(&pcq))) { while (AST_LIST_NEXT(prev, list) && ((AST_LIST_NEXT(prev, list))->expiration <= qe->expiration)) prev = AST_LIST_NEXT(prev, list); AST_LIST_INSERT_AFTER(&pcq, prev, qe, list); } else AST_LIST_INSERT_HEAD(&pcq, qe, list); AST_LIST_UNLOCK(&pcq); } static void dundi_precache_full(void) { struct dundi_mapping *cur; struct ast_context *con; struct ast_exten *e; AST_LIST_TRAVERSE(&mappings, cur, list) { ast_log(LOG_NOTICE, "Should precache context '%s'\n", cur->dcontext); ast_rdlock_contexts(); con = ast_walk_contexts(NULL); while (con) { if (!strcasecmp(cur->lcontext, ast_get_context_name(con))) { /* Found the match, now queue them all up */ ast_lock_context(con); e = ast_walk_context_extensions(con, NULL); while (e) { reschedule_precache(ast_get_extension_name(e), cur->dcontext, 0); e = ast_walk_context_extensions(con, e); } ast_unlock_context(con); } con = ast_walk_contexts(con); } ast_unlock_contexts(); } } static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[]) { struct dundi_request dr; struct dundi_hint_metadata hmd; struct dundi_result dr2[MAX_RESULTS]; struct timeval start; struct dundi_mapping *maps = NULL, *cur; int nummaps = 0; int foundanswers; int foundcache, skipped, ttlms, ms; if (!context) context = "e164"; ast_log(LOG_DEBUG, "Precache internal (%s@%s)!\n", number, context); AST_LIST_LOCK(&peers); AST_LIST_TRAVERSE(&mappings, cur, list) { if (!strcasecmp(cur->dcontext, context)) nummaps++; } if (nummaps) { maps = alloca(nummaps * sizeof(*maps)); nummaps = 0; if (maps) { AST_LIST_TRAVERSE(&mappings, cur, list) { if (!strcasecmp(cur->dcontext, context)) maps[nummaps++] = *cur; } } } AST_LIST_UNLOCK(&peers); if (!nummaps || !maps) return -1; ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME; memset(&dr2, 0, sizeof(dr2)); memset(&dr, 0, sizeof(dr)); memset(&hmd, 0, sizeof(hmd)); dr.dr = dr2; ast_copy_string(dr.number, number, sizeof(dr.number)); ast_copy_string(dr.dcontext, context ? context : "e164", sizeof(dr.dcontext)); dr.maxcount = MAX_RESULTS; dr.expiration = dundi_cache_time; dr.hmd = &hmd; dr.pfds[0] = dr.pfds[1] = -1; pipe(dr.pfds); build_transactions(&dr, ttl, 0, &foundcache, &skipped, 0, 1, 1, NULL, avoids, NULL); optimize_transactions(&dr, 0); foundanswers = 0; precache_transactions(&dr, maps, nummaps, &dr.expiration, &foundanswers); if (foundanswers) { if (dr.expiration > 0) reschedule_precache(dr.number, dr.dcontext, dr.expiration); else ast_log(LOG_NOTICE, "Weird, expiration = %d, but need to precache for %s@%s?!\n", dr.expiration, dr.number, dr.dcontext); } start = ast_tvnow(); while (!AST_LIST_EMPTY(&dr.trans) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms)) { if (dr.pfds[0] > -1) { ms = 100; ast_waitfor_n_fd(dr.pfds, 1, &ms, NULL); } else usleep(1); } cancel_request(&dr); if (dr.pfds[0] > -1) { close(dr.pfds[0]); close(dr.pfds[1]); } return 0; } int dundi_precache(const char *context, const char *number) { dundi_eid *avoid[1] = { NULL, }; return dundi_precache_internal(context, number, dundi_ttl, avoid); } static int dundi_query_eid_internal(struct dundi_entity_info *dei, const char *dcontext, dundi_eid *eid, struct dundi_hint_metadata *hmd, int ttl, int blockempty, dundi_eid *avoid[]) { int res; struct dundi_request dr; dundi_eid *rooteid=NULL; int x; int ttlms; int skipped=0; int foundcache=0; struct timeval start; ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME; for (x=0;avoid[x];x++) rooteid = avoid[x]; /* Now perform real check */ memset(&dr, 0, sizeof(dr)); dr.hmd = hmd; dr.dei = dei; dr.pfds[0] = dr.pfds[1] = -1; ast_copy_string(dr.dcontext, dcontext ? dcontext : "e164", sizeof(dr.dcontext)); memcpy(&dr.query_eid, eid, sizeof(dr.query_eid)); if (rooteid) dr.root_eid = *rooteid; /* Create transactions */ build_transactions(&dr, ttl, 9999, &foundcache, &skipped, blockempty, 0, 0, NULL, avoid, NULL); /* If no TTL, abort and return 0 now after setting TTL expired hint. Couldn't do this earlier because we didn't know if we were going to have transactions or not. */ if (!ttl) { ast_set_flag_nonstd(hmd, DUNDI_HINT_TTL_EXPIRED); return 0; } /* Optimize transactions */ optimize_transactions(&dr, 9999); /* Actually perform transactions */ query_transactions(&dr); /* Wait for transaction to come back */ start = ast_tvnow(); while (!AST_LIST_EMPTY(&dr.trans) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms)) usleep(1); res = dr.respcount; return res; } int dundi_query_eid(struct dundi_entity_info *dei, const char *dcontext, dundi_eid eid) { dundi_eid *avoid[1] = { NULL, }; struct dundi_hint_metadata hmd; memset(&hmd, 0, sizeof(hmd)); return dundi_query_eid_internal(dei, dcontext, &eid, &hmd, dundi_ttl, 0, avoid); } static int dundifunc_read(struct ast_channel *chan, char *cmd, char *num, char *buf, size_t len) { char *context; char *opts; int results; int x; int bypass = 0; struct ast_module_user *u; struct dundi_result dr[MAX_RESULTS]; buf[0] = '\0'; if (ast_strlen_zero(num)) { ast_log(LOG_WARNING, "DUNDILOOKUP requires an argument (number)\n"); return -1; } u = ast_module_user_add(chan); context = strchr(num, '|'); if (context) { *context++ = '\0'; opts = strchr(context, '|'); if (opts) { *opts++ = '\0'; if (strchr(opts, 'b')) bypass = 1; } } if (ast_strlen_zero(context)) context = "e164"; results = dundi_lookup(dr, MAX_RESULTS, NULL, context, num, bypass); if (results > 0) { sort_results(dr, results); for (x = 0; x < results; x++) { if (ast_test_flag(dr + x, DUNDI_FLAG_EXISTS)) { snprintf(buf, len, "%s/%s", dr[x].tech, dr[x].dest); break; } } } ast_module_user_remove(u); return 0; } /*! DUNDILOOKUP * \ingroup functions */ static struct ast_custom_function dundi_function = { .name = "DUNDILOOKUP", .synopsis = "Do a DUNDi lookup of a phone number.", .syntax = "DUNDILOOKUP(number[|context[|options]])", .desc = "This will do a DUNDi lookup of the given phone number.\n" "If no context is given, the default will be e164. The result of\n" "this function will the Technology/Resource found in the DUNDi\n" "lookup. If no results were found, the result will be blank.\n" "If the 'b' option is specified, the internal DUNDi cache will\n" "be bypassed.\n", .read = dundifunc_read, }; static void mark_peers(void) { struct dundi_peer *peer; AST_LIST_LOCK(&peers); AST_LIST_TRAVERSE(&peers, peer, list) { peer->dead = 1; } AST_LIST_UNLOCK(&peers); } static void mark_mappings(void) { struct dundi_mapping *map; AST_LIST_LOCK(&peers); AST_LIST_TRAVERSE(&mappings, map, list) { map->dead = 1; } AST_LIST_UNLOCK(&peers); } static void destroy_permissions(struct permissionlist *permlist) { struct permission *perm; while ((perm = AST_LIST_REMOVE_HEAD(permlist, list))) free(perm); } static void destroy_peer(struct dundi_peer *peer) { AST_SCHED_DEL(sched, peer->registerid); if (peer->regtrans) destroy_trans(peer->regtrans, 0); AST_SCHED_DEL(sched, peer->qualifyid); destroy_permissions(&peer->permit); destroy_permissions(&peer->include); free(peer); } static void destroy_map(struct dundi_mapping *map) { free(map); } static void prune_peers(void) { struct dundi_peer *peer; AST_LIST_LOCK(&peers); AST_LIST_TRAVERSE_SAFE_BEGIN(&peers, peer, list) { if (peer->dead) { AST_LIST_REMOVE_CURRENT(&peers, list); destroy_peer(peer); } } AST_LIST_TRAVERSE_SAFE_END AST_LIST_UNLOCK(&peers); } static void prune_mappings(void) { struct dundi_mapping *map; AST_LIST_LOCK(&peers); AST_LIST_TRAVERSE_SAFE_BEGIN(&mappings, map, list) { if (map->dead) { AST_LIST_REMOVE_CURRENT(&mappings, list); destroy_map(map); } } AST_LIST_TRAVERSE_SAFE_END AST_LIST_UNLOCK(&peers); } static void append_permission(struct permissionlist *permlist, char *s, int allow) { struct permission *perm; if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(s) + 1))) return; strcpy(perm->name, s); perm->allow = allow; AST_LIST_INSERT_TAIL(permlist, perm, list); } #define MAX_OPTS 128 static void build_mapping(char *name, char *value) { char *t, *fields[MAX_OPTS]; struct dundi_mapping *map; int x; int y; t = ast_strdupa(value); AST_LIST_TRAVERSE(&mappings, map, list) { /* Find a double match */ if (!strcasecmp(map->dcontext, name) && (!strncasecmp(map->lcontext, value, strlen(map->lcontext)) && (!value[strlen(map->lcontext)] || (value[strlen(map->lcontext)] == ',')))) break; } if (!map) { if (!(map = ast_calloc(1, sizeof(*map)))) return; AST_LIST_INSERT_HEAD(&mappings, map, list); map->dead = 1; } map->options = 0; memset(fields, 0, sizeof(fields)); x = 0; while (t && x < MAX_OPTS) { fields[x++] = t; t = strchr(t, ','); if (t) { *t = '\0'; t++; } } /* Russell was here, arrrr! */ if ((x == 1) && ast_strlen_zero(fields[0])) { /* Placeholder mapping */ ast_copy_string(map->dcontext, name, sizeof(map->dcontext)); map->dead = 0; } else if (x >= 4) { ast_copy_string(map->dcontext, name, sizeof(map->dcontext)); ast_copy_string(map->lcontext, fields[0], sizeof(map->lcontext)); if ((sscanf(fields[1], "%d", &map->weight) == 1) && (map->weight >= 0) && (map->weight < 60000)) { ast_copy_string(map->dest, fields[3], sizeof(map->dest)); if ((map->tech = str2tech(fields[2]))) { map->dead = 0; } } else { ast_log(LOG_WARNING, "Invalid weight '%s' specified, deleting entry '%s/%s'\n", fields[1], map->dcontext, map->lcontext); } for (y = 4;y < x; y++) { if (!strcasecmp(fields[y], "nounsolicited")) map->options |= DUNDI_FLAG_NOUNSOLICITED; else if (!strcasecmp(fields[y], "nocomunsolicit")) map->options |= DUNDI_FLAG_NOCOMUNSOLICIT; else if (!strcasecmp(fields[y], "residential")) map->options |= DUNDI_FLAG_RESIDENTIAL; else if (!strcasecmp(fields[y], "commercial")) map->options |= DUNDI_FLAG_COMMERCIAL; else if (!strcasecmp(fields[y], "mobile")) map->options |= DUNDI_FLAG_MOBILE; else if (!strcasecmp(fields[y], "nopartial")) map->options |= DUNDI_FLAG_INTERNAL_NOPARTIAL; else ast_log(LOG_WARNING, "Don't know anything about option '%s'\n", fields[y]); } } else ast_log(LOG_WARNING, "Expected at least %d arguments in map, but got only %d\n", 4, x); } /* \note Called with the peers list already locked */ static int do_register(const void *data) { struct dundi_ie_data ied; struct dundi_peer *peer = (struct dundi_peer *)data; char eid_str[20]; char eid_str2[20]; ast_log(LOG_DEBUG, "Register us as '%s' to '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->us_eid), dundi_eid_to_str(eid_str2, sizeof(eid_str2), &peer->eid)); peer->registerid = ast_sched_add(sched, default_expiration * 1000, do_register, data); /* Destroy old transaction if there is one */ if (peer->regtrans) destroy_trans(peer->regtrans, 0); peer->regtrans = create_transaction(peer); if (peer->regtrans) { ast_set_flag(peer->regtrans, FLAG_ISREG); memset(&ied, 0, sizeof(ied)); dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION); dundi_ie_append_eid(&ied, DUNDI_IE_EID, &peer->regtrans->us_eid); dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, default_expiration); dundi_send(peer->regtrans, DUNDI_COMMAND_REGREQ, 0, 0, &ied); } else ast_log(LOG_NOTICE, "Unable to create new transaction for registering to '%s'!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid)); return 0; } static int do_qualify(const void *data) { struct dundi_peer *peer = (struct dundi_peer *)data; peer->qualifyid = -1; qualify_peer(peer, 0); return 0; } static void qualify_peer(struct dundi_peer *peer, int schedonly) { int when; AST_SCHED_DEL(sched, peer->qualifyid); if (peer->qualtrans) destroy_trans(peer->qualtrans, 0); peer->qualtrans = NULL; if (peer->maxms > 0) { when = 60000; if (peer->lastms < 0) when = 10000; if (schedonly) when = 5000; peer->qualifyid = ast_sched_add(sched, when, do_qualify, peer); if (!schedonly) peer->qualtrans = create_transaction(peer); if (peer->qualtrans) { peer->qualtx = ast_tvnow(); ast_set_flag(peer->qualtrans, FLAG_ISQUAL); dundi_send(peer->qualtrans, DUNDI_COMMAND_NULL, 0, 1, NULL); } } } static void populate_addr(struct dundi_peer *peer, dundi_eid *eid) { char data[256]; char *c; int port, expire; char eid_str[20]; dundi_eid_to_str(eid_str, sizeof(eid_str), eid); if (!ast_db_get("dundi/dpeers", eid_str, data, sizeof(data))) { c = strchr(data, ':'); if (c) { *c = '\0'; c++; if (sscanf(c, "%d:%d", &port, &expire) == 2) { /* Got it! */ inet_aton(data, &peer->addr.sin_addr); peer->addr.sin_family = AF_INET; peer->addr.sin_port = htons(port); peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer); } } } } static void build_peer(dundi_eid *eid, struct ast_variable *v, int *globalpcmode) { struct dundi_peer *peer; struct ast_hostent he; struct hostent *hp; dundi_eid testeid; int needregister=0; char eid_str[20]; AST_LIST_LOCK(&peers); AST_LIST_TRAVERSE(&peers, peer, list) { if (!dundi_eid_cmp(&peer->eid, eid)) { break; } } if (!peer) { /* Add us into the list */ if (!(peer = ast_calloc(1, sizeof(*peer)))) { AST_LIST_UNLOCK(&peers); return; } peer->registerid = -1; peer->registerexpire = -1; peer->qualifyid = -1; peer->addr.sin_family = AF_INET; peer->addr.sin_port = htons(DUNDI_PORT); populate_addr(peer, eid); AST_LIST_INSERT_HEAD(&peers, peer, list); } peer->dead = 0; peer->eid = *eid; peer->us_eid = global_eid; destroy_permissions(&peer->permit); destroy_permissions(&peer->include); AST_SCHED_DEL(sched, peer->registerid); for (; v; v = v->next) { if (!strcasecmp(v->name, "inkey")) { ast_copy_string(peer->inkey, v->value, sizeof(peer->inkey)); } else if (!strcasecmp(v->name, "outkey")) { ast_copy_string(peer->outkey, v->value, sizeof(peer->outkey)); } else if (!strcasecmp(v->name, "host")) { if (!strcasecmp(v->value, "dynamic")) { peer->dynamic = 1; } else { hp = ast_gethostbyname(v->value, &he); if (hp) { memcpy(&peer->addr.sin_addr, hp->h_addr, sizeof(peer->addr.sin_addr)); peer->dynamic = 0; } else { ast_log(LOG_WARNING, "Unable to find host '%s' at line %d\n", v->value, v->lineno); peer->dead = 1; } } } else if (!strcasecmp(v->name, "ustothem")) { if (!dundi_str_to_eid(&testeid, v->value)) peer->us_eid = testeid; else ast_log(LOG_WARNING, "'%s' is not a valid DUNDi Entity Identifier at line %d\n", v->value, v->lineno); } else if (!strcasecmp(v->name, "include")) { append_permission(&peer->include, v->value, 1); } else if (!strcasecmp(v->name, "permit")) { append_permission(&peer->permit, v->value, 1); } else if (!strcasecmp(v->name, "noinclude")) { append_permission(&peer->include, v->value, 0); } else if (!strcasecmp(v->name, "deny")) { append_permission(&peer->permit, v->value, 0); } else if (!strcasecmp(v->name, "register")) { needregister = ast_true(v->value); } else if (!strcasecmp(v->name, "order")) { if (!strcasecmp(v->value, "primary")) peer->order = 0; else if (!strcasecmp(v->value, "secondary")) peer->order = 1; else if (!strcasecmp(v->value, "tertiary")) peer->order = 2; else if (!strcasecmp(v->value, "quartiary")) peer->order = 3; else { ast_log(LOG_WARNING, "'%s' is not a valid order, should be primary, secondary, tertiary or quartiary at line %d\n", v->value, v->lineno); } } else if (!strcasecmp(v->name, "qualify")) { if (!strcasecmp(v->value, "no")) { peer->maxms = 0; } else if (!strcasecmp(v->value, "yes")) { peer->maxms = DEFAULT_MAXMS; } else if (sscanf(v->value, "%d", &peer->maxms) != 1) { ast_log(LOG_WARNING, "Qualification of peer '%s' should be 'yes', 'no', or a number of milliseconds at line %d of dundi.conf\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), v->lineno); peer->maxms = 0; } } else if (!strcasecmp(v->name, "model")) { if (!strcasecmp(v->value, "inbound")) peer->model = DUNDI_MODEL_INBOUND; else if (!strcasecmp(v->value, "outbound")) peer->model = DUNDI_MODEL_OUTBOUND; else if (!strcasecmp(v->value, "symmetric")) peer->model = DUNDI_MODEL_SYMMETRIC; else if (!strcasecmp(v->value, "none")) peer->model = 0; else { ast_log(LOG_WARNING, "Unknown model '%s', should be 'none', 'outbound', 'inbound', or 'symmetric' at line %d\n", v->value, v->lineno); } } else if (!strcasecmp(v->name, "precache")) { if (!strcasecmp(v->value, "inbound")) peer->pcmodel = DUNDI_MODEL_INBOUND; else if (!strcasecmp(v->value, "outbound")) peer->pcmodel = DUNDI_MODEL_OUTBOUND; else if (!strcasecmp(v->value, "symmetric")) peer->pcmodel = DUNDI_MODEL_SYMMETRIC; else if (!strcasecmp(v->value, "none")) peer->pcmodel = 0; else { ast_log(LOG_WARNING, "Unknown pcmodel '%s', should be 'none', 'outbound', 'inbound', or 'symmetric' at line %d\n", v->value, v->lineno); } } } (*globalpcmode) |= peer->pcmodel; if (!peer->model && !peer->pcmodel) { ast_log(LOG_WARNING, "Peer '%s' lacks a model or pcmodel, discarding!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid)); peer->dead = 1; } else if ((peer->model & DUNDI_MODEL_INBOUND) && (peer->pcmodel & DUNDI_MODEL_OUTBOUND)) { ast_log(LOG_WARNING, "Peer '%s' may not be both inbound/symmetric model and outbound/symmetric precache model, discarding!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid)); peer->dead = 1; } else if ((peer->model & DUNDI_MODEL_OUTBOUND) && (peer->pcmodel & DUNDI_MODEL_INBOUND)) { ast_log(LOG_WARNING, "Peer '%s' may not be both outbound/symmetric model and inbound/symmetric precache model, discarding!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid)); peer->dead = 1; } else if (!AST_LIST_EMPTY(&peer->include) && !(peer->model & DUNDI_MODEL_OUTBOUND) && !(peer->pcmodel & DUNDI_MODEL_INBOUND)) { ast_log(LOG_WARNING, "Peer '%s' is supposed to be included in outbound searches but isn't an outbound peer or inbound precache!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid)); } else if (!AST_LIST_EMPTY(&peer->permit) && !(peer->model & DUNDI_MODEL_INBOUND) && !(peer->pcmodel & DUNDI_MODEL_OUTBOUND)) { ast_log(LOG_WARNING, "Peer '%s' is supposed to have permission for some inbound searches but isn't an inbound peer or outbound precache!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid)); } else { if (needregister) { peer->registerid = ast_sched_add(sched, 2000, do_register, peer); } qualify_peer(peer, 1); } AST_LIST_UNLOCK(&peers); } static int dundi_helper(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *data, int flag) { struct dundi_result results[MAX_RESULTS]; int res; int x; int found = 0; if (!strncasecmp(context, "macro-", 6)) { if (!chan) { ast_log(LOG_NOTICE, "Can't use macro mode without a channel!\n"); return -1; } /* If done as a macro, use macro extension */ if (!strcasecmp(exten, "s")) { exten = pbx_builtin_getvar_helper(chan, "ARG1"); if (ast_strlen_zero(exten)) exten = chan->macroexten; if (ast_strlen_zero(exten)) exten = chan->exten; if (ast_strlen_zero(exten)) { ast_log(LOG_WARNING, "Called in Macro mode with no ARG1 or MACRO_EXTEN?\n"); return -1; } } if (ast_strlen_zero(data)) data = "e164"; } else { if (ast_strlen_zero(data)) data = context; } res = dundi_lookup(results, MAX_RESULTS, chan, data, exten, 0); for (x=0;x= priority) return 1; return 0; } static int dundi_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data) { return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_EXISTS); } static int dundi_canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data) { return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_CANMATCH); } static int dundi_exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data) { struct dundi_result results[MAX_RESULTS]; int res; int x=0; char req[1024]; struct ast_app *dial; if (!strncasecmp(context, "macro-", 6)) { if (!chan) { ast_log(LOG_NOTICE, "Can't use macro mode without a channel!\n"); return -1; } /* If done as a macro, use macro extension */ if (!strcasecmp(exten, "s")) { exten = pbx_builtin_getvar_helper(chan, "ARG1"); if (ast_strlen_zero(exten)) exten = chan->macroexten; if (ast_strlen_zero(exten)) exten = chan->exten; if (ast_strlen_zero(exten)) { ast_log(LOG_WARNING, "Called in Macro mode with no ARG1 or MACRO_EXTEN?\n"); return -1; } } if (ast_strlen_zero(data)) data = "e164"; } else { if (ast_strlen_zero(data)) data = context; } res = dundi_lookup(results, MAX_RESULTS, chan, data, exten, 0); if (res > 0) { sort_results(results, res); for (x=0;xh_addr, sizeof(sin2.sin_addr)); ast_copy_string(ipaddr, ast_inet_ntoa(sin2.sin_addr), sizeof(ipaddr)); } else ast_log(LOG_WARNING, "Unable to look up host '%s'\n", hn); } else ast_log(LOG_WARNING, "Unable to get host name!\n"); AST_LIST_LOCK(&peers); reset_global_eid(); global_storehistory = 0; ast_copy_string(secretpath, "dundi", sizeof(secretpath)); v = ast_variable_browse(cfg, "general"); while(v) { if (!strcasecmp(v->name, "port")){ sin->sin_port = ntohs(atoi(v->value)); if(last_port==0){ last_port=sin->sin_port; } else if(sin->sin_port != last_port) ast_log(LOG_WARNING, "change to port ignored until next asterisk re-start\n"); } else if (!strcasecmp(v->name, "bindaddr")) { struct hostent *hp; struct ast_hostent he; hp = ast_gethostbyname(v->value, &he); if (hp) { memcpy(&sin->sin_addr, hp->h_addr, sizeof(sin->sin_addr)); } else ast_log(LOG_WARNING, "Invalid host/IP '%s'\n", v->value); } else if (!strcasecmp(v->name, "authdebug")) { authdebug = ast_true(v->value); } else if (!strcasecmp(v->name, "ttl")) { if ((sscanf(v->value, "%d", &x) == 1) && (x > 0) && (x < DUNDI_DEFAULT_TTL)) { dundi_ttl = x; } else { ast_log(LOG_WARNING, "'%s' is not a valid TTL at line %d, must be number from 1 to %d\n", v->value, v->lineno, DUNDI_DEFAULT_TTL); } } else if (!strcasecmp(v->name, "autokill")) { if (sscanf(v->value, "%d", &x) == 1) { if (x >= 0) global_autokilltimeout = x; else ast_log(LOG_NOTICE, "Nice try, but autokill has to be >0 or 'yes' or 'no' at line %d\n", v->lineno); } else if (ast_true(v->value)) { global_autokilltimeout = DEFAULT_MAXMS; } else { global_autokilltimeout = 0; } } else if (!strcasecmp(v->name, "entityid")) { if (!dundi_str_to_eid(&testeid, v->value)) global_eid = testeid; else ast_log(LOG_WARNING, "Invalid global endpoint identifier '%s' at line %d\n", v->value, v->lineno); } else if (!strcasecmp(v->name, "tos")) { if (sscanf(v->value, "%d", &format) == 1) tos = format & 0xff; else if (!strcasecmp(v->value, "lowdelay")) tos = IPTOS_LOWDELAY; else if (!strcasecmp(v->value, "throughput")) tos = IPTOS_THROUGHPUT; else if (!strcasecmp(v->value, "reliability")) tos = IPTOS_RELIABILITY; #if !defined(__NetBSD__) && !defined(__OpenBSD__) && !defined(SOLARIS) else if (!strcasecmp(v->value, "mincost")) tos = IPTOS_MINCOST; #endif else if (!strcasecmp(v->value, "none")) tos = 0; else #if !defined(__NetBSD__) && !defined(__OpenBSD__) && !defined(SOLARIS) ast_log(LOG_WARNING, "Invalid tos value at line %d, should be 'lowdelay', 'throughput', 'reliability', 'mincost', or 'none'\n", v->lineno); #else ast_log(LOG_WARNING, "Invalid tos value at line %d, should be 'lowdelay', 'throughput', 'reliability', or 'none'\n", v->lineno); #endif } else if (!strcasecmp(v->name, "department")) { ast_copy_string(dept, v->value, sizeof(dept)); } else if (!strcasecmp(v->name, "organization")) { ast_copy_string(org, v->value, sizeof(org)); } else if (!strcasecmp(v->name, "locality")) { ast_copy_string(locality, v->value, sizeof(locality)); } else if (!strcasecmp(v->name, "stateprov")) { ast_copy_string(stateprov, v->value, sizeof(stateprov)); } else if (!strcasecmp(v->name, "country")) { ast_copy_string(country, v->value, sizeof(country)); } else if (!strcasecmp(v->name, "email")) { ast_copy_string(email, v->value, sizeof(email)); } else if (!strcasecmp(v->name, "phone")) { ast_copy_string(phone, v->value, sizeof(phone)); } else if (!strcasecmp(v->name, "storehistory")) { global_storehistory = ast_true(v->value); } else if (!strcasecmp(v->name, "cachetime")) { if ((sscanf(v->value, "%d", &x) == 1)) { dundi_cache_time = x; } else { ast_log(LOG_WARNING, "'%s' is not a valid cache time at line %d. Using default value '%d'.\n", v->value, v->lineno, DUNDI_DEFAULT_CACHE_TIME); } } v = v->next; } AST_LIST_UNLOCK(&peers); mark_mappings(); v = ast_variable_browse(cfg, "mappings"); while(v) { build_mapping(v->name, v->value); v = v->next; } prune_mappings(); mark_peers(); cat = ast_category_browse(cfg, NULL); while(cat) { if (strcasecmp(cat, "general") && strcasecmp(cat, "mappings")) { /* Entries */ if (!dundi_str_to_eid(&testeid, cat)) build_peer(&testeid, ast_variable_browse(cfg, cat), &globalpcmodel); else if (!strcasecmp(cat, "*")) { build_peer(&empty_eid, ast_variable_browse(cfg, cat), &globalpcmodel); any_peer = find_peer(NULL); } else ast_log(LOG_NOTICE, "Ignoring invalid EID entry '%s'\n", cat); } cat = ast_category_browse(cfg, cat); } prune_peers(); ast_config_destroy(cfg); load_password(); if (globalpcmodel & DUNDI_MODEL_OUTBOUND) dundi_precache_full(); return 0; } static int unload_module(void) { pthread_t previous_netthreadid = netthreadid, previous_precachethreadid = precachethreadid; ast_module_user_hangup_all(); /* Stop all currently running threads */ dundi_shutdown = 1; if (previous_netthreadid != AST_PTHREADT_NULL) { pthread_kill(previous_netthreadid, SIGURG); pthread_join(previous_netthreadid, NULL); } if (previous_precachethreadid != AST_PTHREADT_NULL) { pthread_kill(previous_precachethreadid, SIGURG); pthread_join(previous_precachethreadid, NULL); } ast_cli_unregister_multiple(cli_dundi, sizeof(cli_dundi) / sizeof(struct ast_cli_entry)); ast_unregister_switch(&dundi_switch); ast_custom_function_unregister(&dundi_function); close(netsocket); io_context_destroy(io); sched_context_destroy(sched); mark_mappings(); prune_mappings(); mark_peers(); prune_peers(); return 0; } static int reload(void) { struct sockaddr_in sin; set_config("dundi.conf",&sin); return 0; } static int load_module(void) { int res = 0; struct sockaddr_in sin; dundi_set_output(dundi_debug_output); dundi_set_error(dundi_error_output); sin.sin_family = AF_INET; sin.sin_port = ntohs(DUNDI_PORT); sin.sin_addr.s_addr = INADDR_ANY; /* Make a UDP socket */ io = io_context_create(); sched = sched_context_create(); if (!io || !sched) { ast_log(LOG_ERROR, "Out of memory\n"); return -1; } if(set_config("dundi.conf",&sin)) return AST_MODULE_LOAD_DECLINE; netsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); if (netsocket < 0) { ast_log(LOG_ERROR, "Unable to create network socket: %s\n", strerror(errno)); return -1; } if (bind(netsocket,(struct sockaddr *)&sin, sizeof(sin))) { ast_log(LOG_ERROR, "Unable to bind to %s port %d: %s\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), strerror(errno)); return -1; } if (option_verbose > 1) ast_verbose(VERBOSE_PREFIX_2 "Using TOS bits %d\n", tos); if (setsockopt(netsocket, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))) ast_log(LOG_WARNING, "Unable to set TOS to %d\n", tos); res = start_network_thread(); if (res) { ast_log(LOG_ERROR, "Unable to start network thread\n"); close(netsocket); return -1; } if (option_verbose > 1) ast_verbose(VERBOSE_PREFIX_2 "DUNDi Ready and Listening on %s port %d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); ast_cli_register_multiple(cli_dundi, sizeof(cli_dundi) / sizeof(struct ast_cli_entry)); if (ast_register_switch(&dundi_switch)) ast_log(LOG_ERROR, "Unable to register DUNDi switch\n"); ast_custom_function_register(&dundi_function); return res; } AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Distributed Universal Number Discovery (DUNDi)", .load = load_module, .unload = unload_module, .reload = reload, ); asterisk-1.4.21.2/pbx/pbx_ael.c0000644000175000017500000041605411021255012015374 0ustar maniacmaniac/* * Asterisk -- An open source telephony toolkit. * * Copyright (C) 2006, Digium, Inc. * * Steve Murphy * * 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 Compile symbolic Asterisk Extension Logic into Asterisk extensions, version 2. * */ #include "asterisk.h" ASTERISK_FILE_VERSION(__FILE__, "$Revision: 119929 $") #include #include #include #include #include #include #include #include #include #include "asterisk/pbx.h" #include "asterisk/config.h" #include "asterisk/module.h" #include "asterisk/logger.h" #include "asterisk/cli.h" #include "asterisk/app.h" #include "asterisk/callerid.h" #include "asterisk/ael_structs.h" #ifdef AAL_ARGCHECK #include "asterisk/argdesc.h" #endif static char expr_output[2096]; /* these functions are in ../ast_expr2.fl */ #define DEBUG_READ (1 << 0) #define DEBUG_TOKENS (1 << 1) #define DEBUG_MACROS (1 << 2) #define DEBUG_CONTEXTS (1 << 3) static char *config = "extensions.ael"; static char *registrar = "pbx_ael"; static int pbx_load_module(void); static int errs, warns; static int notes; #ifndef AAL_ARGCHECK /* for the time being, short circuit all the AAL related structures without permanently removing the code; after/during the AAL development, this code can be properly re-instated */ /* null definitions for structs passed down the infrastructure */ struct argapp { struct argapp *next; }; #endif #ifdef AAL_ARGCHECK int option_matches_j( struct argdesc *should, pval *is, struct argapp *app); int option_matches( struct argdesc *should, pval *is, struct argapp *app); int ael_is_funcname(char *name); #endif int check_app_args(pval *appcall, pval *arglist, struct argapp *app); void check_pval(pval *item, struct argapp *apps, int in_globals); void check_pval_item(pval *item, struct argapp *apps, int in_globals); void check_switch_expr(pval *item, struct argapp *apps); void ast_expr_register_extra_error_info(char *errmsg); void ast_expr_clear_extra_error_info(void); int ast_expr(char *expr, char *buf, int length); struct pval *find_macro(char *name); struct pval *find_context(char *name); struct pval *find_context(char *name); struct pval *find_macro(char *name); struct ael_priority *new_prio(void); struct ael_extension *new_exten(void); void linkprio(struct ael_extension *exten, struct ael_priority *prio, struct ael_extension *mother_exten); void destroy_extensions(struct ael_extension *exten); static void linkexten(struct ael_extension *exten, struct ael_extension *add); static void gen_prios(struct ael_extension *exten, char *label, pval *statement, struct ael_extension *mother_exten, struct ast_context *context ); void set_priorities(struct ael_extension *exten); void add_extensions(struct ael_extension *exten); void ast_compile_ael2(struct ast_context **local_contexts, struct pval *root); void destroy_pval(pval *item); void destroy_pval_item(pval *item); int is_float(char *arg ); int is_int(char *arg ); int is_empty(char *arg); static pval *current_db=0; static pval *current_context=0; static pval *current_extension=0; static const char *match_context; static const char *match_exten; static const char *match_label; static int in_abstract_context; static int count_labels; /* true, put matcher in label counting mode */ static int label_count; /* labels are only meant to be counted in a context or exten */ static int return_on_context_match; static pval *last_matched_label; struct pval *match_pval(pval *item); static void check_timerange(pval *p); static void check_dow(pval *DOW); static void check_day(pval *DAY); static void check_month(pval *MON); static void check_expr2_input(pval *expr, char *str); static int extension_matches(pval *here, const char *exten, const char *pattern); static void check_goto(pval *item); static void find_pval_goto_item(pval *item, int lev); static void find_pval_gotos(pval *item, int lev); static struct pval *find_label_in_current_context(char *exten, char *label, pval *curr_cont); static struct pval *find_first_label_in_current_context(char *label, pval *curr_cont); static void print_pval_list(FILE *fin, pval *item, int depth); static struct pval *find_label_in_current_extension(const char *label, pval *curr_ext); static struct pval *find_label_in_current_db(const char *context, const char *exten, const char *label); static pval *get_goto_target(pval *item); static int label_inside_case(pval *label); static void attach_exten(struct ael_extension **list, struct ael_extension *newmem); static void fix_gotos_in_extensions(struct ael_extension *exten); static pval *get_extension_or_contxt(pval *p); static pval *get_contxt(pval *p); static void remove_spaces_before_equals(char *str); static void substitute_commas(char *str); /* I am adding this code to substitute commas with vertbars in the args to apps */ static void substitute_commas(char *str) { char *p = str; while (p && *p) { if (*p == ',' && ((p != str && *(p-1) != '\\') || p == str)) *p = '|'; if (*p == '\\' && *(p+1) == ',') { /* learning experience: the '\,' is turned into just ',' by pbx_config; So we need to do the same */ char *q = p; while (*q) { /* move the ',' and everything after it up 1 char */ *q = *(q+1); q++; } } p++; } } /* PRETTY PRINTER FOR AEL: ============================================================================= */ static void print_pval(FILE *fin, pval *item, int depth) { int i; pval *lp; for (i=0; itype ) { case PV_WORD: fprintf(fin,"%s;\n", item->u1.str); /* usually, words are encapsulated in something else */ break; case PV_MACRO: fprintf(fin,"macro %s(", item->u1.str); for (lp=item->u2.arglist; lp; lp=lp->next) { if (lp != item->u2.arglist ) fprintf(fin,", "); fprintf(fin,"%s", lp->u1.str); } fprintf(fin,") {\n"); print_pval_list(fin,item->u3.macro_statements,depth+1); for (i=0; iu3.abstract ) fprintf(fin,"abstract context %s {\n", item->u1.str); else fprintf(fin,"context %s {\n", item->u1.str); print_pval_list(fin,item->u2.statements,depth+1); for (i=0; iu1.str); for (lp=item->u2.arglist; lp; lp=lp->next) { if ( lp != item->u2.arglist ) fprintf(fin,", "); fprintf(fin,"%s", lp->u1.str); } fprintf(fin,");\n"); break; case PV_APPLICATION_CALL: fprintf(fin,"%s(", item->u1.str); for (lp=item->u2.arglist; lp; lp=lp->next) { if ( lp != item->u2.arglist ) fprintf(fin,","); fprintf(fin,"%s", lp->u1.str); } fprintf(fin,");\n"); break; case PV_CASE: fprintf(fin,"case %s:\n", item->u1.str); print_pval_list(fin,item->u2.statements, depth+1); break; case PV_PATTERN: fprintf(fin,"pattern %s:\n", item->u1.str); print_pval_list(fin,item->u2.statements, depth+1); break; case PV_DEFAULT: fprintf(fin,"default:\n"); print_pval_list(fin,item->u2.statements, depth+1); break; case PV_CATCH: fprintf(fin,"catch %s {\n", item->u1.str); print_pval_list(fin,item->u2.statements, depth+1); for (i=0; iu1.list,depth+1); for (i=0; iu1.list,depth+1); for (i=0; iu1.list; lp; lp=lp->next) { for (i=0; iu1.str); /* usually, words are encapsulated in something else */ if ( lp->u2.arglist ) fprintf(fin,"|%s|%s|%s|%s", lp->u2.arglist->u1.str, lp->u2.arglist->next->u1.str, lp->u2.arglist->next->next->u1.str, lp->u2.arglist->next->next->next->u1.str ); fprintf(fin,";\n"); /* usually, words are encapsulated in something else */ } print_pval_list(fin,item->u1.list,depth+1); for (i=0; iu1.list, depth+1); for (i=0; iu1.str, item->u2.val); break; case PV_GOTO: fprintf(fin,"goto %s", item->u1.list->u1.str); if ( item->u1.list->next ) fprintf(fin,"|%s", item->u1.list->next->u1.str); if ( item->u1.list->next && item->u1.list->next->next ) fprintf(fin,"|%s", item->u1.list->next->next->u1.str); fprintf(fin,"\n"); break; case PV_LABEL: fprintf(fin,"%s:\n", item->u1.str); break; case PV_FOR: fprintf(fin,"for (%s; %s; %s)\n", item->u1.for_init, item->u2.for_test, item->u3.for_inc); print_pval_list(fin,item->u4.for_statements,depth+1); break; case PV_WHILE: fprintf(fin,"while (%s)\n", item->u1.str); print_pval_list(fin,item->u2.statements,depth+1); break; case PV_BREAK: fprintf(fin,"break;\n"); break; case PV_RETURN: fprintf(fin,"return;\n"); break; case PV_CONTINUE: fprintf(fin,"continue;\n"); break; case PV_RANDOM: case PV_IFTIME: case PV_IF: if ( item->type == PV_IFTIME ) { fprintf(fin,"ifTime ( %s|%s|%s|%s )\n", item->u1.list->u1.str, item->u1.list->next->u1.str, item->u1.list->next->next->u1.str, item->u1.list->next->next->next->u1.str ); } else if ( item->type == PV_RANDOM ) { fprintf(fin,"random ( %s )\n", item->u1.str ); } else fprintf(fin,"if ( %s )\n", item->u1.str); if ( item->u2.statements && item->u2.statements->next ) { for (i=0; iu2.statements,depth+1); for (i=0; iu3.else_statements ) fprintf(fin,"}\n"); else fprintf(fin,"};\n"); } else if (item->u2.statements ) { print_pval_list(fin,item->u2.statements,depth+1); } else { if (item->u3.else_statements ) fprintf(fin, " {} "); else fprintf(fin, " {}; "); } if ( item->u3.else_statements ) { for (i=0; iu3.else_statements, depth); } break; case PV_SWITCH: fprintf(fin,"switch( %s ) {\n", item->u1.str); print_pval_list(fin,item->u2.statements,depth+1); for (i=0; iu4.regexten ) fprintf(fin, "regexten "); if ( item->u3.hints ) fprintf(fin,"hints(%s) ", item->u3.hints); fprintf(fin,"%s => \n", item->u1.str); print_pval_list(fin,item->u2.statements,depth+1); break; case PV_IGNOREPAT: fprintf(fin,"ignorepat => %s\n", item->u1.str); break; case PV_GLOBALS: fprintf(fin,"globals {\n"); print_pval_list(fin,item->u1.statements,depth+1); for (i=0; inext) { print_pval(fin, i, depth); } } #if 0 static void ael2_print(char *fname, pval *tree) { FILE *fin = fopen(fname,"w"); if ( !fin ) { ast_log(LOG_ERROR, "Couldn't open %s for writing.\n", fname); return; } print_pval_list(fin, tree, 0); fclose(fin); } #endif /* EMPTY TEMPLATE FUNCS FOR AEL TRAVERSAL: ============================================================================= */ void traverse_pval_template(pval *item, int depth); void traverse_pval_item_template(pval *item, int depth); void traverse_pval_item_template(pval *item, int depth)/* depth comes in handy for a pretty print (indentation), but you may not need it */ { pval *lp; switch ( item->type ) { case PV_WORD: /* fields: item->u1.str == string associated with this (word). */ break; case PV_MACRO: /* fields: item->u1.str == name of macro item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user item->u2.arglist->u1.str == argument item->u2.arglist->next == next arg item->u3.macro_statements == pval list of statements in macro body. */ for (lp=item->u2.arglist; lp; lp=lp->next) { } traverse_pval_item_template(item->u3.macro_statements,depth+1); break; case PV_CONTEXT: /* fields: item->u1.str == name of context item->u2.statements == pval list of statements in context body item->u3.abstract == int 1 if an abstract keyword were present */ traverse_pval_item_template(item->u2.statements,depth+1); break; case PV_MACRO_CALL: /* fields: item->u1.str == name of macro to call item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user item->u2.arglist->u1.str == argument item->u2.arglist->next == next arg */ for (lp=item->u2.arglist; lp; lp=lp->next) { } break; case PV_APPLICATION_CALL: /* fields: item->u1.str == name of application to call item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user item->u2.arglist->u1.str == argument item->u2.arglist->next == next arg */ for (lp=item->u2.arglist; lp; lp=lp->next) { } break; case PV_CASE: /* fields: item->u1.str == value of case item->u2.statements == pval list of statements under the case */ traverse_pval_item_template(item->u2.statements,depth+1); break; case PV_PATTERN: /* fields: item->u1.str == value of case item->u2.statements == pval list of statements under the case */ traverse_pval_item_template(item->u2.statements,depth+1); break; case PV_DEFAULT: /* fields: item->u2.statements == pval list of statements under the case */ traverse_pval_item_template(item->u2.statements,depth+1); break; case PV_CATCH: /* fields: item->u1.str == name of extension to catch item->u2.statements == pval list of statements in context body */ traverse_pval_item_template(item->u2.statements,depth+1); break; case PV_SWITCHES: /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list */ traverse_pval_item_template(item->u1.list,depth+1); break; case PV_ESWITCHES: /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list */ traverse_pval_item_template(item->u1.list,depth+1); break; case PV_INCLUDES: /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list item->u2.arglist == pval list of 4 PV_WORD elements for time values */ traverse_pval_item_template(item->u1.list,depth+1); traverse_pval_item_template(item->u2.arglist,depth+1); break; case PV_STATEMENTBLOCK: /* fields: item->u1.list == pval list of statements in block, one per entry in the list */ traverse_pval_item_template(item->u1.list,depth+1); break; case PV_VARDEC: /* fields: item->u1.str == variable name item->u2.val == variable value to assign */ break; case PV_GOTO: /* fields: item->u1.list == pval list of PV_WORD target names, up to 3, in order as given by user. item->u1.list->u1.str == where the data on a PV_WORD will always be. */ if ( item->u1.list->next ) ; if ( item->u1.list->next && item->u1.list->next->next ) ; break; case PV_LABEL: /* fields: item->u1.str == label name */ break; case PV_FOR: /* fields: item->u1.for_init == a string containing the initalizer item->u2.for_test == a string containing the loop test item->u3.for_inc == a string containing the loop increment item->u4.for_statements == a pval list of statements in the for () */ traverse_pval_item_template(item->u4.for_statements,depth+1); break; case PV_WHILE: /* fields: item->u1.str == the while conditional, as supplied by user item->u2.statements == a pval list of statements in the while () */ traverse_pval_item_template(item->u2.statements,depth+1); break; case PV_BREAK: /* fields: none */ break; case PV_RETURN: /* fields: none */ break; case PV_CONTINUE: /* fields: none */ break; case PV_IFTIME: /* fields: item->u1.list == there are 4 linked PV_WORDs here. item->u2.statements == a pval list of statements in the if () item->u3.else_statements == a pval list of statements in the else (could be zero) */ traverse_pval_item_template(item->u2.statements,depth+1); if ( item->u3.else_statements ) { traverse_pval_item_template(item->u3.else_statements,depth+1); } break; case PV_RANDOM: /* fields: item->u1.str == the random number expression, as supplied by user item->u2.statements == a pval list of statements in the if () item->u3.else_statements == a pval list of statements in the else (could be zero) */ traverse_pval_item_template(item->u2.statements,depth+1); if ( item->u3.else_statements ) { traverse_pval_item_template(item->u3.else_statements,depth+1); } break; case PV_IF: /* fields: item->u1.str == the if conditional, as supplied by user item->u2.statements == a pval list of statements in the if () item->u3.else_statements == a pval list of statements in the else (could be zero) */ traverse_pval_item_template(item->u2.statements,depth+1); if ( item->u3.else_statements ) { traverse_pval_item_template(item->u3.else_statements,depth+1); } break; case PV_SWITCH: /* fields: item->u1.str == the switch expression item->u2.statements == a pval list of statements in the switch, (will be case statements, most likely!) */ traverse_pval_item_template(item->u2.statements,depth+1); break; case PV_EXTENSION: /* fields: item->u1.str == the extension name, label, whatever it's called item->u2.statements == a pval list of statements in the extension item->u3.hints == a char * hint argument item->u4.regexten == an int boolean. non-zero says that regexten was specified */ traverse_pval_item_template(item->u2.statements,depth+1); break; case PV_IGNOREPAT: /* fields: item->u1.str == the ignorepat data */ break; case PV_GLOBALS: /* fields: item->u1.statements == pval list of statements, usually vardecs */ traverse_pval_item_template(item->u1.statements,depth+1); break; } } void traverse_pval_template(pval *item, int depth) /* depth comes in handy for a pretty print (indentation), but you may not need it */ { pval *i; for (i=item; i; i=i->next) { traverse_pval_item_template(i, depth); } } /* SEMANTIC CHECKING FOR AEL: ============================================================================= */ /* (not all that is syntactically legal is good! */ static int extension_matches(pval *here, const char *exten, const char *pattern) { int err1; regex_t preg; /* simple case, they match exactly, the pattern and exten name */ if( !strcmp(pattern,exten) == 0 ) return 1; if ( pattern[0] == '_' ) { char reg1[2000]; const char *p; char *r = reg1; if ( strlen(pattern)*5 >= 2000 ) /* safety valve */ { ast_log(LOG_ERROR,"Error: The pattern %s is way too big. Pattern matching cancelled.\n", pattern); return 0; } /* form a regular expression from the pattern, and then match it against exten */ *r++ = '^'; /* what if the extension is a pattern ?? */ *r++ = '_'; /* what if the extension is a pattern ?? */ *r++ = '?'; for (p=pattern+1; *p; p++) { switch ( *p ) { case 'X': *r++ = '['; *r++ = '0'; *r++ = '-'; *r++ = '9'; *r++ = 'X'; *r++ = ']'; break; case 'Z': *r++ = '['; *r++ = '1'; *r++ = '-'; *r++ = '9'; *r++ = 'Z'; *r++ = ']'; break; case 'N': *r++ = '['; *r++ = '2'; *r++ = '-'; *r++ = '9'; *r++ = 'N'; *r++ = ']'; break; case '[': while ( *p && *p != ']' ) { *r++ = *p++; } if ( *p != ']') { ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The extension pattern '%s' is missing a closing bracket \n", here->filename, here->startline, here->endline, pattern); } break; case '.': case '!': *r++ = '.'; *r++ = '*'; break; case '*': *r++ = '\\'; *r++ = '*'; break; default: *r++ = *p; break; } } *r++ = '$'; /* what if the extension is a pattern ?? */ *r++ = *p++; /* put in the closing null */ err1 = regcomp(&preg, reg1, REG_NOSUB|REG_EXTENDED); if ( err1 ) { char errmess[500]; regerror(err1,&preg,errmess,sizeof(errmess)); regfree(&preg); ast_log(LOG_WARNING, "Regcomp of %s failed, error code %d\n", reg1, err1); return 0; } err1 = regexec(&preg, exten, 0, 0, 0); regfree(&preg); if ( err1 ) { /* ast_log(LOG_NOTICE,"*****************************[%d]Extension %s did not match %s(%s)\n", err1,exten, pattern, reg1); */ return 0; /* no match */ } else { /* ast_log(LOG_NOTICE,"*****************************Extension %s matched %s\n", exten, pattern); */ return 1; } } else { if ( strcmp(exten,pattern) == 0 ) { return 1; } else return 0; } } static void check_expr2_input(pval *expr, char *str) { int spaces = strspn(str,"\t \n"); if ( !strncmp(str+spaces,"$[",2) ) { ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The expression '%s' is redundantly wrapped in '$[ ]'. \n", expr->filename, expr->startline, expr->endline, str); warns++; } } static void check_includes(pval *includes) { struct pval *p4; for (p4=includes->u1.list; p4; p4=p4->next) { /* for each context pointed to, find it, then find a context/label that matches the target here! */ char *incl_context = p4->u1.str; /* find a matching context name */ struct pval *that_other_context = find_context(incl_context); if (!that_other_context && strcmp(incl_context, "parkedcalls") != 0) { ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The included context '%s' cannot be found.\n\ (You may ignore this warning if '%s' exists in extensions.conf, or is created by another module. I cannot check for those.)\n", includes->filename, includes->startline, includes->endline, incl_context, incl_context); warns++; } } } static void check_timerange(pval *p) { char *times; char *e; int s1, s2; int e1, e2; times = ast_strdupa(p->u1.str); /* Star is all times */ if (ast_strlen_zero(times) || !strcmp(times, "*")) { return; } /* Otherwise expect a range */ e = strchr(times, '-'); if (!e) { ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The time range format (%s) requires a '-' surrounded by two 24-hour times of day!\n", p->filename, p->startline, p->endline, times); warns++; return; } *e = '\0'; e++; while (*e && !isdigit(*e)) e++; if (!*e) { ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The time range format (%s) is missing the end time!\n", p->filename, p->startline, p->endline, p->u1.str); warns++; } if (sscanf(times, "%d:%d", &s1, &s2) != 2) { ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start time (%s) isn't quite right!\n", p->filename, p->startline, p->endline, times); warns++; } if (sscanf(e, "%d:%d", &e1, &e2) != 2) { ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end time (%s) isn't quite right!\n", p->filename, p->startline, p->endline, times); warns++; } s1 = s1 * 30 + s2/2; if ((s1 < 0) || (s1 >= 24*30)) { ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start time (%s) is out of range!\n", p->filename, p->startline, p->endline, times); warns++; } e1 = e1 * 30 + e2/2; if ((e1 < 0) || (e1 >= 24*30)) { ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end time (%s) is out of range!\n", p->filename, p->startline, p->endline, e); warns++; } return; } static char *days[] = { "sun", "mon", "tue", "wed", "thu", "fri", "sat", }; /*! \brief get_dow: Get day of week */ static void check_dow(pval *DOW) { char *dow; char *c; /* The following line is coincidence, really! */ int s, e; dow = ast_strdupa(DOW->u1.str); /* Check for all days */ if (ast_strlen_zero(dow) || !strcmp(dow, "*")) return; /* Get start and ending days */ c = strchr(dow, '-'); if (c) { *c = '\0'; c++; } else c = NULL; /* Find the start */ s = 0; while ((s < 7) && strcasecmp(dow, days[s])) s++; if (s >= 7) { ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The day (%s) must be one of 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', or 'sat'!\n", DOW->filename, DOW->startline, DOW->endline, dow); warns++; } if (c) { e = 0; while ((e < 7) && strcasecmp(c, days[e])) e++; if (e >= 7) { ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end day (%s) must be one of 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', or 'sat'!\n", DOW->filename, DOW->startline, DOW->endline, c); warns++; } } else e = s; } static void check_day(pval *DAY) { char *day; char *c; /* The following line is coincidence, really! */ int s, e; day = ast_strdupa(DAY->u1.str); /* Check for all days */ if (ast_strlen_zero(day) || !strcmp(day, "*")) { return; } /* Get start and ending days */ c = strchr(day, '-'); if (c) { *c = '\0'; c++; } /* Find the start */ if (sscanf(day, "%d", &s) != 1) { ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start day of month (%s) must be a number!\n", DAY->filename, DAY->startline, DAY->endline, day); warns++; } else if ((s < 1) || (s > 31)) { ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start day of month (%s) must be a number in the range [1-31]!\n", DAY->filename, DAY->startline, DAY->endline, day); warns++; } s--; if (c) { if (sscanf(c, "%d", &e) != 1) { ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end day of month (%s) must be a number!\n", DAY->filename, DAY->startline, DAY->endline, c); warns++; } else if ((e < 1) || (e > 31)) { ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end day of month (%s) must be a number in the range [1-31]!\n", DAY->filename, DAY->startline, DAY->endline, day); warns++; } e--; } else e = s; } static char *months[] = { "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec", }; static void check_month(pval *MON) { char *mon; char *c; /* The following line is coincidence, really! */ int s, e; mon = ast_strdupa(MON->u1.str); /* Check for all days */ if (ast_strlen_zero(mon) || !strcmp(mon, "*")) return ; /* Get start and ending days */ c = strchr(mon, '-'); if (c) { *c = '\0'; c++; } /* Find the start */ s = 0; while ((s < 12) && strcasecmp(mon, months[s])) s++; if (s >= 12) { ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start month (%s) must be a one of: 'jan', 'feb', ..., 'dec'!\n", MON->filename, MON->startline, MON->endline, mon); warns++; } if (c) { e = 0; while ((e < 12) && strcasecmp(mon, months[e])) e++; if (e >= 12) { ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end month (%s) must be a one of: 'jan', 'feb', ..., 'dec'!\n", MON->filename, MON->startline, MON->endline, c); warns++; } } else e = s; } static int check_break(pval *item) { pval *p = item; while( p && p->type != PV_MACRO && p->type != PV_CONTEXT ) /* early cutout, sort of */ { /* a break is allowed in WHILE, FOR, CASE, DEFAULT, PATTERN; otherwise, it don't make no sense */ if( p->type == PV_CASE || p->type == PV_DEFAULT || p->type == PV_PATTERN || p->type == PV_WHILE || p->type == PV_FOR ) { return 1; } p = p->dad; } ast_log(LOG_ERROR,"Error: file %s, line %d-%d: 'break' not in switch, for, or while statement!\n", item->filename, item->startline, item->endline); errs++; return 0; } static int check_continue(pval *item) { pval *p = item; while( p && p->type != PV_MACRO && p->type != PV_CONTEXT ) /* early cutout, sort of */ { /* a break is allowed in WHILE, FOR, CASE, DEFAULT, PATTERN; otherwise, it don't make no sense */ if( p->type == PV_WHILE || p->type == PV_FOR ) { return 1; } p = p->dad; } ast_log(LOG_ERROR,"Error: file %s, line %d-%d: 'continue' not in 'for' or 'while' statement!\n", item->filename, item->startline, item->endline); errs++; return 0; } /* general purpose goto finder */ static void check_label(pval *item) { /* basically, ensure that a label is not repeated in a context. Period. The method: well, for each label, find the first label in the context with the same name. If it's not the current label, then throw an error. */ struct pval *curr; struct pval *x; /* printf("==== check_label: ====\n"); */ if( !current_extension ) curr = current_context; else curr = current_extension; x = find_first_label_in_current_context((char *)item->u1.str, curr); /* printf("Hey, check_label found with item = %x, and x is %x, and currcont is %x, label name is %s\n", item,x, current_context, (char *)item->u1.str); */ if( x && x != item ) { ast_log(LOG_ERROR,"Error: file %s, line %d-%d: Duplicate label %s! Previously defined at file %s, line %d.\n", item->filename, item->startline, item->endline, item->u1.str, x->filename, x->startline); errs++; } /* printf("<<<<< check_label: ====\n"); */ } static pval *get_goto_target(pval *item) { /* just one item-- the label should be in the current extension */ pval *curr_ext = get_extension_or_contxt(item); /* containing exten, or macro */ pval *curr_cont; if (item->u1.list && !item->u1.list->next && !strstr((item->u1.list)->u1.str,"${")) { struct pval *x = find_label_in_current_extension((char*)((item->u1.list)->u1.str), curr_ext); return x; } curr_cont = get_contxt(item); /* TWO items */ if (item->u1.list->next && !item->u1.list->next->next) { if (!strstr((item->u1.list)->u1.str,"${") && !strstr(item->u1.list->next->u1.str,"${") ) /* Don't try to match variables */ { struct pval *x = find_label_in_current_context((char *)item->u1.list->u1.str, (char *)item->u1.list->next->u1.str, curr_cont); return x; } } /* All 3 items! */ if (item->u1.list->next && item->u1.list->next->next) { /* all three */ pval *first = item->u1.list; pval *second = item->u1.list->next; pval *third = item->u1.list->next->next; if (!strstr((item->u1.list)->u1.str,"${") && !strstr(item->u1.list->next->u1.str,"${") && !strstr(item->u1.list->next->next->u1.str,"${")) /* Don't try to match variables */ { struct pval *x = find_label_in_current_db((char*)first->u1.str, (char*)second->u1.str, (char*)third->u1.str); if (!x) { struct pval *p3; struct pval *that_context = find_context(item->u1.list->u1.str); /* the target of the goto could be in an included context!! Fancy that!! */ /* look for includes in the current context */ if (that_context) { for (p3=that_context->u2.statements; p3; p3=p3->next) { if (p3->type == PV_INCLUDES) { struct pval *p4; for (p4=p3->u1.list; p4; p4=p4->next) { /* for each context pointed to, find it, then find a context/label that matches the target here! */ char *incl_context = p4->u1.str; /* find a matching context name */ struct pval *that_other_context = find_context(incl_context); if (that_other_context) { struct pval *x3; x3 = find_label_in_current_context((char *)item->u1.list->next->u1.str, (char *)item->u1.list->next->next->u1.str, that_other_context); if (x3) { return x3; } } } } } } } return x; } } return 0; } static void check_goto(pval *item) { /* check for the target of the goto-- does it exist? */ if ( !(item->u1.list)->next && !(item->u1.list)->u1.str ) { ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto: empty label reference found!\n", item->filename, item->startline, item->endline); errs++; } /* just one item-- the label should be in the current extension */ if (item->u1.list && !item->u1.list->next && !strstr((item->u1.list)->u1.str,"${")) { struct pval *z = get_extension_or_contxt(item); struct pval *x = 0; if (z) x = find_label_in_current_extension((char*)((item->u1.list)->u1.str), z); /* if in macro, use current context instead */ /* printf("Called find_label_in_current_extension with arg %s; current_extension is %x: %d\n", (char*)((item->u1.list)->u1.str), current_extension?current_extension:current_context, current_extension?current_extension->type:current_context->type); */ if (!x) { ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto: no label %s exists in the current extension!\n", item->filename, item->startline, item->endline, item->u1.list->u1.str); errs++; } else return; } /* TWO items */ if (item->u1.list->next && !item->u1.list->next->next) { /* two items */ /* printf("Calling find_label_in_current_context with args %s, %s\n", (char*)((item->u1.list)->u1.str), (char *)item->u1.list->next->u1.str); */ if (!strstr((item->u1.list)->u1.str,"${") && !strstr(item->u1.list->next->u1.str,"${") ) /* Don't try to match variables */ { struct pval *z = get_contxt(item); struct pval *x = 0; if (z) x = find_label_in_current_context((char *)item->u1.list->u1.str, (char *)item->u1.list->next->u1.str, z); if (!x) { ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto: no label %s|%s exists in the current context, or any of its inclusions!\n", item->filename, item->startline, item->endline, item->u1.list->u1.str, item->u1.list->next->u1.str ); errs++; } else return; } } /* All 3 items! */ if (item->u1.list->next && item->u1.list->next->next) { /* all three */ pval *first = item->u1.list; pval *second = item->u1.list->next; pval *third = item->u1.list->next->next; /* printf("Calling find_label_in_current_db with args %s, %s, %s\n", (char*)first->u1.str, (char*)second->u1.str, (char*)third->u1.str); */ if (!strstr((item->u1.list)->u1.str,"${") && !strstr(item->u1.list->next->u1.str,"${") && !strstr(item->u1.list->next->next->u1.str,"${")) /* Don't try to match variables */ { struct pval *x = find_label_in_current_db((char*)first->u1.str, (char*)second->u1.str, (char*)third->u1.str); if (!x) { struct pval *p3; struct pval *found = 0; struct pval *that_context = find_context(item->u1.list->u1.str); /* the target of the goto could be in an included context!! Fancy that!! */ /* look for includes in the current context */ if (that_context) { for (p3=that_context->u2.statements; p3; p3=p3->next) { if (p3->type == PV_INCLUDES) { struct pval *p4; for (p4=p3->u1.list; p4; p4=p4->next) { /* for each context pointed to, find it, then find a context/label that matches the target here! */ char *incl_context = p4->u1.str; /* find a matching context name */ struct pval *that_other_context = find_context(incl_context); if (that_other_context) { struct pval *x3; x3 = find_label_in_current_context((char *)item->u1.list->next->u1.str, (char *)item->u1.list->next->next->u1.str, that_other_context); if (x3) { found = x3; break; } } } } } if (!found) { ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto: no label %s|%s exists in the context %s or its inclusions!\n", item->filename, item->startline, item->endline, item->u1.list->next->u1.str, item->u1.list->next->next->u1.str, item->u1.list->u1.str ); errs++; } } else { /* here is where code would go to check for target existence in extensions.conf files */ ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: goto: no context %s could be found that matches the goto target!\n", item->filename, item->startline, item->endline, item->u1.list->u1.str); warns++; /* this is just a warning, because this context could be in extensions.conf or somewhere */ } } } } } static void find_pval_goto_item(pval *item, int lev) { struct pval *p4; if (lev>100) { ast_log(LOG_ERROR,"find_pval_goto in infinite loop!\n\n"); return; } switch ( item->type ) { case PV_MACRO: /* fields: item->u1.str == name of macro item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user item->u2.arglist->u1.str == argument item->u2.arglist->next == next arg item->u3.macro_statements == pval list of statements in macro body. */ /* printf("Descending into matching macro %s\n", match_context); */ find_pval_gotos(item->u3.macro_statements,lev+1); /* if we're just searching for a context, don't bother descending into them */ break; case PV_CONTEXT: /* fields: item->u1.str == name of context item->u2.statements == pval list of statements in context body item->u3.abstract == int 1 if an abstract keyword were present */ break; case PV_CASE: /* fields: item->u1.str == value of case item->u2.statements == pval list of statements under the case */ find_pval_gotos(item->u2.statements,lev+1); break; case PV_PATTERN: /* fields: item->u1.str == value of case item->u2.statements == pval list of statements under the case */ find_pval_gotos(item->u2.statements,lev+1); break; case PV_DEFAULT: /* fields: item->u2.statements == pval list of statements under the case */ find_pval_gotos(item->u2.statements,lev+1); break; case PV_CATCH: /* fields: item->u1.str == name of extension to catch item->u2.statements == pval list of statements in context body */ find_pval_gotos(item->u2.statements,lev+1); break; case PV_STATEMENTBLOCK: /* fields: item->u1.list == pval list of statements in block, one per entry in the list */ find_pval_gotos(item->u1.list,lev+1); break; case PV_GOTO: /* fields: item->u1.list == pval list of PV_WORD target names, up to 3, in order as given by user. item->u1.list->u1.str == where the data on a PV_WORD will always be. */ check_goto(item); /* THE WHOLE FUNCTION OF THIS ENTIRE ROUTINE!!!! */ break; case PV_INCLUDES: /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list */ for (p4=item->u1.list; p4; p4=p4->next) { /* for each context pointed to, find it, then find a context/label that matches the target here! */ char *incl_context = p4->u1.str; /* find a matching context name */ struct pval *that_context = find_context(incl_context); if (that_context) { find_pval_gotos(that_context,lev+1); /* keep working up the includes */ } } break; case PV_FOR: /* fields: item->u1.for_init == a string containing the initalizer item->u2.for_test == a string containing the loop test item->u3.for_inc == a string containing the loop increment item->u4.for_statements == a pval list of statements in the for () */ find_pval_gotos(item->u4.for_statements,lev+1); break; case PV_WHILE: /* fields: item->u1.str == the while conditional, as supplied by user item->u2.statements == a pval list of statements in the while () */ find_pval_gotos(item->u2.statements,lev+1); break; case PV_RANDOM: /* fields: item->u1.str == the random number expression, as supplied by user item->u2.statements == a pval list of statements in the if () item->u3.else_statements == a pval list of statements in the else (could be zero) fall thru to PV_IF */ case PV_IFTIME: /* fields: item->u1.list == the time values, 4 of them, as PV_WORD structs in a list item->u2.statements == a pval list of statements in the if () item->u3.else_statements == a pval list of statements in the else (could be zero) fall thru to PV_IF*/ case PV_IF: /* fields: item->u1.str == the if conditional, as supplied by user item->u2.statements == a pval list of statements in the if () item->u3.else_statements == a pval list of statements in the else (could be zero) */ find_pval_gotos(item->u2.statements,lev+1); if (item->u3.else_statements) { find_pval_gotos(item->u3.else_statements,lev+1); } break; case PV_SWITCH: /* fields: item->u1.str == the switch expression item->u2.statements == a pval list of statements in the switch, (will be case statements, most likely!) */ find_pval_gotos(item->u3.else_statements,lev+1); break; case PV_EXTENSION: /* fields: item->u1.str == the extension name, label, whatever it's called item->u2.statements == a pval list of statements in the extension item->u3.hints == a char * hint argument item->u4.regexten == an int boolean. non-zero says that regexten was specified */ find_pval_gotos(item->u2.statements,lev+1); break; default: break; } } static void find_pval_gotos(pval *item,int lev) { pval *i; for (i=item; i; i=i->next) { find_pval_goto_item(i, lev); } } /* general purpose label finder */ static struct pval *match_pval_item(pval *item) { pval *x; switch ( item->type ) { case PV_MACRO: /* fields: item->u1.str == name of macro item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user item->u2.arglist->u1.str == argument item->u2.arglist->next == next arg item->u3.macro_statements == pval list of statements in macro body. */ /* printf(" matching in MACRO %s, match_context=%s; retoncontmtch=%d; \n", item->u1.str, match_context, return_on_context_match); */ if (!strcmp(match_context,"*") || !strcmp(item->u1.str, match_context)) { /* printf("MACRO: match context is: %s\n", match_context); */ if (return_on_context_match && !strcmp(item->u1.str, match_context)) /* if we're just searching for a context, don't bother descending into them */ { /* printf("Returning on matching macro %s\n", match_context); */ return item; } if (!return_on_context_match) { /* printf("Descending into matching macro %s/%s\n", match_context, item->u1.str); */ if ((x=match_pval(item->u3.macro_statements))) { /* printf("Responded with pval match %x\n", x); */ return x; } } } else { /* printf("Skipping context/macro %s\n", item->u1.str); */ } break; case PV_CONTEXT: /* fields: item->u1.str == name of context item->u2.statements == pval list of statements in context body item->u3.abstract == int 1 if an abstract keyword were present */ /* printf(" matching in CONTEXT\n"); */ if (!strcmp(match_context,"*") || !strcmp(item->u1.str, match_context)) { if (return_on_context_match && !strcmp(item->u1.str, match_context)) { /* printf("Returning on matching context %s\n", match_context); */ /* printf("non-CONTEXT: Responded with pval match %x\n", x); */ return item; } if (!return_on_context_match ) { /* printf("Descending into matching context %s\n", match_context); */ if ((x=match_pval(item->u2.statements))) /* if we're just searching for a context, don't bother descending into them */ { /* printf("CONTEXT: Responded with pval match %x\n", x); */ return x; } } } else { /* printf("Skipping context/macro %s\n", item->u1.str); */ } break; case PV_CASE: /* fields: item->u1.str == value of case item->u2.statements == pval list of statements under the case */ /* printf(" matching in CASE\n"); */ if ((x=match_pval(item->u2.statements))) { /* printf("CASE: Responded with pval match %x\n", x); */ return x; } break; case PV_PATTERN: /* fields: item->u1.str == value of case item->u2.statements == pval list of statements under the case */ /* printf(" matching in PATTERN\n"); */ if ((x=match_pval(item->u2.statements))) { /* printf("PATTERN: Responded with pval match %x\n", x); */ return x; } break; case PV_DEFAULT: /* fields: item->u2.statements == pval list of statements under the case */ /* printf(" matching in DEFAULT\n"); */ if ((x=match_pval(item->u2.statements))) { /* printf("DEFAULT: Responded with pval match %x\n", x); */ return x; } break; case PV_CATCH: /* fields: item->u1.str == name of extension to catch item->u2.statements == pval list of statements in context body */ /* printf(" matching in CATCH\n"); */ if (!strcmp(match_exten,"*") || extension_matches(item, match_exten, item->u1.str) ) { /* printf("Descending into matching catch %s => %s\n", match_exten, item->u1.str); */ if (strcmp(match_label,"1") == 0) { if (item->u2.statements) { struct pval *p5 = item->u2.statements; while (p5 && p5->type == PV_LABEL) /* find the first non-label statement in this context. If it exists, there's a "1" */ p5 = p5->next; if (p5) return p5; else return 0; } else return 0; } if ((x=match_pval(item->u2.statements))) { /* printf("CATCH: Responded with pval match %x\n", (unsigned int)x); */ return x; } } else { /* printf("Skipping catch %s\n", item->u1.str); */ } break; case PV_STATEMENTBLOCK: /* fields: item->u1.list == pval list of statements in block, one per entry in the list */ /* printf(" matching in STATEMENTBLOCK\n"); */ if ((x=match_pval(item->u1.list))) { /* printf("STATEMENTBLOCK: Responded with pval match %x\n", x); */ return x; } break; case PV_LABEL: /* fields: item->u1.str == label name */ /* printf("PV_LABEL %s (cont=%s, exten=%s\n", item->u1.str, current_context->u1.str, (current_extension?current_extension->u1.str:""));*/ if (count_labels) { if (!strcmp(match_label, item->u1.str)) { label_count++; last_matched_label = item; } } else { if (!strcmp(match_label, item->u1.str)) { /* printf("LABEL: Responded with pval match %x\n", x); */ return item; } } break; case PV_FOR: /* fields: item->u1.for_init == a string containing the initalizer item->u2.for_test == a string containing the loop test item->u3.for_inc == a string containing the loop increment item->u4.for_statements == a pval list of statements in the for () */ /* printf(" matching in FOR\n"); */ if ((x=match_pval(item->u4.for_statements))) { /* printf("FOR: Responded with pval match %x\n", x);*/ return x; } break; case PV_WHILE: /* fields: item->u1.str == the while conditional, as supplied by user item->u2.statements == a pval list of statements in the while () */ /* printf(" matching in WHILE\n"); */ if ((x=match_pval(item->u2.statements))) { /* printf("WHILE: Responded with pval match %x\n", x); */ return x; } break; case PV_RANDOM: /* fields: item->u1.str == the random number expression, as supplied by user item->u2.statements == a pval list of statements in the if () item->u3.else_statements == a pval list of statements in the else (could be zero) fall thru to PV_IF */ case PV_IFTIME: /* fields: item->u1.list == the time values, 4 of them, as PV_WORD structs in a list item->u2.statements == a pval list of statements in the if () item->u3.else_statements == a pval list of statements in the else (could be zero) fall thru to PV_IF*/ case PV_IF: /* fields: item->u1.str == the if conditional, as supplied by user item->u2.statements == a pval list of statements in the if () item->u3.else_statements == a pval list of statements in the else (could be zero) */ /* printf(" matching in IF/IFTIME/RANDOM\n"); */ if ((x=match_pval(item->u2.statements))) { return x; } if (item->u3.else_statements) { if ((x=match_pval(item->u3.else_statements))) { /* printf("IF/IFTIME/RANDOM: Responded with pval match %x\n", x); */ return x; } } break; case PV_SWITCH: /* fields: item->u1.str == the switch expression item->u2.statements == a pval list of statements in the switch, (will be case statements, most likely!) */ /* printf(" matching in SWITCH\n"); */ if ((x=match_pval(item->u2.statements))) { /* printf("SWITCH: Responded with pval match %x\n", x); */ return x; } break; case PV_EXTENSION: /* fields: item->u1.str == the extension name, label, whatever it's called item->u2.statements == a pval list of statements in the extension item->u3.hints == a char * hint argument item->u4.regexten == an int boolean. non-zero says that regexten was specified */ /* printf(" matching in EXTENSION\n"); */ if (!strcmp(match_exten,"*") || extension_matches(item, match_exten, item->u1.str) ) { /* printf("Descending into matching exten %s => %s\n", match_exten, item->u1.str); */ if (strcmp(match_label,"1") == 0) { if (item->u2.statements) { struct pval *p5 = item->u2.statements; while (p5 && p5->type == PV_LABEL) /* find the first non-label statement in this context. If it exists, there's a "1" */ p5 = p5->next; if (p5) return p5; else return 0; } else return 0; } if ((x=match_pval(item->u2.statements))) { /* printf("EXTENSION: Responded with pval match %x\n", x); */ return x; } } else { /* printf("Skipping exten %s\n", item->u1.str); */ } break; default: /* printf(" matching in default = %d\n", item->type); */ break; } return 0; } struct pval *match_pval(pval *item) { pval *i; for (i=item; i; i=i->next) { pval *x; /* printf(" -- match pval: item %d\n", i->type); */ if ((x = match_pval_item(i))) { /* printf("match_pval: returning x=%x\n", (int)x); */ return x; /* cut the search short */ } } return 0; } #if 0 int count_labels_in_current_context(char *label) { label_count = 0; count_labels = 1; return_on_context_match = 0; match_pval(current_context->u2.statements); return label_count; } #endif struct pval *find_first_label_in_current_context(char *label, pval *curr_cont) { /* printf(" --- Got args %s, %s\n", exten, label); */ struct pval *ret; struct pval *p3; struct pval *startpt = ((curr_cont->type==PV_MACRO)?curr_cont->u3.macro_statements: curr_cont->u2.statements); count_labels = 0; return_on_context_match = 0; match_context = "*"; match_exten = "*"; match_label = label; ret = match_pval(curr_cont); if (ret) return ret; /* the target of the goto could be in an included context!! Fancy that!! */ /* look for includes in the current context */ for (p3=startpt; p3; p3=p3->next) { if (p3->type == PV_INCLUDES) { struct pval *p4; for (p4=p3->u1.list; p4; p4=p4->next) { /* for each context pointed to, find it, then find a context/label that matches the target here! */ char *incl_context = p4->u1.str; /* find a matching context name */ struct pval *that_context = find_context(incl_context); if (that_context) { struct pval *x3; x3 = find_first_label_in_current_context(label, that_context); if (x3) { return x3; } } } } } return 0; } struct pval *find_label_in_current_context(char *exten, char *label, pval *curr_cont) { /* printf(" --- Got args %s, %s\n", exten, label); */ struct pval *ret; struct pval *p3; struct pval *startpt; count_labels = 0; return_on_context_match = 0; match_context = "*"; match_exten = exten; match_label = label; if (curr_cont->type == PV_MACRO) startpt = curr_cont->u3.macro_statements; else startpt = curr_cont->u2.statements; ret = match_pval(startpt); if (ret) return ret; /* the target of the goto could be in an included context!! Fancy that!! */ /* look for includes in the current context */ for (p3=startpt; p3; p3=p3->next) { if (p3->type == PV_INCLUDES) { struct pval *p4; for (p4=p3->u1.list; p4; p4=p4->next) { /* for each context pointed to, find it, then find a context/label that matches the target here! */ char *incl_context = p4->u1.str; /* find a matching context name */ struct pval *that_context = find_context(incl_context); if (that_context) { struct pval *x3; x3 = find_label_in_current_context(exten, label, that_context); if (x3) { return x3; } } } } } return 0; } static struct pval *find_label_in_current_extension(const char *label, pval *curr_ext) { /* printf(" --- Got args %s\n", label); */ count_labels = 0; return_on_context_match = 0; match_context = "*"; match_exten = "*"; match_label = label; return match_pval(curr_ext); } static struct pval *find_label_in_current_db(const char *context, const char *exten, const char *label) { /* printf(" --- Got args %s, %s, %s\n", context, exten, label); */ count_labels = 0; return_on_context_match = 0; match_context = context; match_exten = exten; match_label = label; return match_pval(current_db); } struct pval *find_macro(char *name) { return_on_context_match = 1; count_labels = 0; match_context = name; match_exten = "*"; /* don't really need to set these, shouldn't be reached */ match_label = "*"; return match_pval(current_db); } struct pval *find_context(char *name) { return_on_context_match = 1; count_labels = 0; match_context = name; match_exten = "*"; /* don't really need to set these, shouldn't be reached */ match_label = "*"; return match_pval(current_db); } int is_float(char *arg ) { char *s; for (s=arg; *s; s++) { if (*s != '.' && (*s < '0' || *s > '9')) return 0; } return 1; } int is_int(char *arg ) { char *s; for (s=arg; *s; s++) { if (*s < '0' || *s > '9') return 0; } return 1; } int is_empty(char *arg) { if (!arg) return 1; if (*arg == 0) return 1; while (*arg) { if (*arg != ' ' && *arg != '\t') return 0; arg++; } return 1; } #ifdef AAL_ARGCHECK int option_matches_j( struct argdesc *should, pval *is, struct argapp *app) { struct argchoice *ac; char *opcop,*q,*p; switch (should->dtype) { case ARGD_OPTIONSET: if ( strstr(is->u1.str,"${") ) return 0; /* no checking anything if there's a var reference in there! */ opcop = ast_strdupa(is->u1.str); for (q=opcop;*q;q++) { /* erase the innards of X(innard) type arguments, so we don't get confused later */ if ( *q == '(' ) { p = q+1; while (*p && *p != ')' ) *p++ = '+'; q = p+1; } } for (ac=app->opts; ac; ac=ac->next) { if (strlen(ac->name)>1 && strchr(ac->name,'(') == 0 && strcmp(ac->name,is->u1.str) == 0) /* multichar option, no parens, and a match? */ return 0; } for (ac=app->opts; ac; ac=ac->next) { if (strlen(ac->name)==1 || strchr(ac->name,'(')) { char *p = strchr(opcop,ac->name[0]); /* wipe out all matched options in the user-supplied string */ if (p && *p == 'j') { ast_log(LOG_ERROR, "Error: file %s, line %d-%d: The j option in the %s application call is not appropriate for AEL!\n", is->filename, is->startline, is->endline, app->name); errs++; } if (p) { *p = '+'; if (ac->name[1] == '(') { if (*(p+1) != '(') { ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The %c option in the %s application call should have an (argument), but doesn't!\n", is->filename, is->startline, is->endline, ac->name[0], app->name); warns++; } } } } } for (q=opcop; *q; q++) { if ( *q != '+' && *q != '(' && *q != ')') { ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The %c option in the %s application call is not available as an option!\n", is->filename, is->startline, is->endline, *q, app->name); warns++; } } return 1; break; default: return 0; } } int option_matches( struct argdesc *should, pval *is, struct argapp *app) { struct argchoice *ac; char *opcop; switch (should->dtype) { case ARGD_STRING: if (is_empty(is->u1.str) && should->type == ARGD_REQUIRED) return 0; if (is->u1.str && strlen(is->u1.str) > 0) /* most will match */ return 1; break; case ARGD_INT: if (is_int(is->u1.str)) return 1; else return 0; break; case ARGD_FLOAT: if (is_float(is->u1.str)) return 1; else return 0; break; case ARGD_ENUM: if( !is->u1.str || strlen(is->u1.str) == 0 ) return 1; /* a null arg in the call will match an enum, I guess! */ for (ac=should->choices; ac; ac=ac->next) { if (strcmp(ac->name,is->u1.str) == 0) return 1; } return 0; break; case ARGD_OPTIONSET: opcop = ast_strdupa(is->u1.str); for (ac=app->opts; ac; ac=ac->next) { if (strlen(ac->name)>1 && strchr(ac->name,'(') == 0 && strcmp(ac->name,is->u1.str) == 0) /* multichar option, no parens, and a match? */ return 1; } for (ac=app->opts; ac; ac=ac->next) { if (strlen(ac->name)==1 || strchr(ac->name,'(')) { char *p = strchr(opcop,ac->name[0]); /* wipe out all matched options in the user-supplied string */ if (p) { *p = '+'; if (ac->name[1] == '(') { if (*(p+1) == '(') { char *q = p+1; while (*q && *q != ')') { *q++ = '+'; } *q = '+'; } } } } } return 1; break; case ARGD_VARARG: return 1; /* matches anything */ break; } return 1; /* unless some for-sure match or non-match returns, then it must be close enough ... */ } #endif int check_app_args(pval* appcall, pval *arglist, struct argapp *app) { #ifdef AAL_ARGCHECK struct argdesc *ad = app->args; pval *pa; int z; for (pa = arglist; pa; pa=pa->next) { if (!ad) { ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: Extra argument %s not in application call to %s !\n", arglist->filename, arglist->startline, arglist->endline, pa->u1.str, app->name); warns++; return 1; } else { /* find the first entry in the ad list that will match */ do { if ( ad->dtype == ARGD_VARARG ) /* once we hit the VARARG, all bets are off. Discontinue the comparisons */ break; z= option_matches( ad, pa, app); if (!z) { if ( !arglist ) arglist=appcall; if (ad->type == ARGD_REQUIRED) { ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: Required argument %s not in application call to %s !\n", arglist->filename, arglist->startline, arglist->endline, ad->dtype==ARGD_OPTIONSET?"options":ad->name, app->name); warns++; return 1; } } else if (z && ad->dtype == ARGD_OPTIONSET) { option_matches_j( ad, pa, app); } ad = ad->next; } while (ad && !z); } } /* any app nodes left, that are not optional? */ for ( ; ad; ad=ad->next) { if (ad->type == ARGD_REQUIRED && ad->dtype != ARGD_VARARG) { if ( !arglist ) arglist=appcall; ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: Required argument %s not in application call to %s !\n", arglist->filename, arglist->startline, arglist->endline, ad->dtype==ARGD_OPTIONSET?"options":ad->name, app->name); warns++; return 1; } } return 0; #else return 0; #endif } void check_switch_expr(pval *item, struct argapp *apps) { #ifdef AAL_ARGCHECK /* get and clean the variable name */ char *buff1, *p; struct argapp *a,*a2; struct appsetvar *v,*v2; struct argchoice *c; pval *t; p = item->u1.str; while (p && *p && (*p == ' ' || *p == '\t' || *p == '$' || *p == '{' ) ) p++; buff1 = ast_strdupa(p); while (strlen(buff1) > 0 && ( buff1[strlen(buff1)-1] == '}' || buff1[strlen(buff1)-1] == ' ' || buff1[strlen(buff1)-1] == '\t')) buff1[strlen(buff1)-1] = 0; /* buff1 now contains the variable name */ v = 0; for (a=apps; a; a=a->next) { for (v=a->setvars;v;v=v->next) { if (strcmp(v->name,buff1) == 0) { break; } } if ( v ) break; } if (v && v->vals) { /* we have a match, to a variable that has a set of determined values */ int def= 0; int pat = 0; int f1 = 0; /* first of all, does this switch have a default case ? */ for (t=item->u2.statements; t; t=t->next) { if (t->type == PV_DEFAULT) { def =1; break; } if (t->type == PV_PATTERN) { pat++; } } if (def || pat) /* nothing to check. All cases accounted for! */ return; for (c=v->vals; c; c=c->next) { f1 = 0; for (t=item->u2.statements; t; t=t->next) { if (t->type == PV_CASE || t->type == PV_PATTERN) { if (!strcmp(t->u1.str,c->name)) { f1 = 1; break; } } } if (!f1) { ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: switch with expression(%s) does not handle the case of %s !\n", item->filename, item->startline, item->endline, item->u1.str, c->name); warns++; } } /* next, is there an app call in the current exten, that would set this var? */ f1 = 0; t = current_extension->u2.statements; if ( t && t->type == PV_STATEMENTBLOCK ) t = t->u1.statements; for (; t && t != item; t=t->next) { if (t->type == PV_APPLICATION_CALL) { /* find the application that matches the u1.str */ for (a2=apps; a2; a2=a2->next) { if (strcasecmp(a2->name, t->u1.str)==0) { for (v2=a2->setvars; v2; v2=v2->next) { if (strcmp(v2->name, buff1) == 0) { /* found an app that sets the var */ f1 = 1; break; } } } if (f1) break; } } if (f1) break; } /* see if it sets the var */ if (!f1) { ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: Couldn't find an application call in this extension that sets the expression (%s) value!\n", item->filename, item->startline, item->endline, item->u1.str); warns++; } } #else pval *t,*tl=0,*p2; int def= 0; /* first of all, does this switch have a default case ? */ for (t=item->u2.statements; t; t=t->next) { if (t->type == PV_DEFAULT) { def =1; break; } tl = t; } if (def) /* nothing to check. All cases accounted for! */ return; /* if no default, warn and insert a default case at the end */ p2 = tl->next = calloc(1, sizeof(struct pval)); p2->type = PV_DEFAULT; p2->startline = tl->startline; p2->endline = tl->endline; p2->startcol = tl->startcol; p2->endcol = tl->endcol; p2->filename = strdup(tl->filename); ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: A default case was automatically added to the switch.\n", p2->filename, p2->startline, p2->endline); warns++; #endif } static void check_context_names(void) { pval *i,*j; for (i=current_db; i; i=i->next) { if (i->type == PV_CONTEXT || i->type == PV_MACRO) { for (j=i->next; j; j=j->next) { if ( j->type == PV_CONTEXT || j->type == PV_MACRO ) { if ( !strcmp(i->u1.str, j->u1.str) && !(i->u3.abstract&2) && !(j->u3.abstract&2) ) { ast_log(LOG_ERROR,"Error: file %s, line %d-%d: The context name (%s) is also declared in file %s, line %d-%d! (and neither is marked 'extend')\n", i->filename, i->startline, i->endline, i->u1.str, j->filename, j->startline, j->endline); errs++; } } } } } } static void check_abstract_reference(pval *abstract_context) { pval *i,*j; /* find some context includes that reference this context */ /* otherwise, print out a warning */ for (i=current_db; i; i=i->next) { if (i->type == PV_CONTEXT) { for (j=i->u2. statements; j; j=j->next) { if ( j->type == PV_INCLUDES ) { struct pval *p4; for (p4=j->u1.list; p4; p4=p4->next) { /* for each context pointed to, find it, then find a context/label that matches the target here! */ if ( !strcmp(p4->u1.str, abstract_context->u1.str) ) return; /* found a match! */ } } } } } ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: Couldn't find a reference to this abstract context (%s) in any other context!\n", abstract_context->filename, abstract_context->startline, abstract_context->endline, abstract_context->u1.str); warns++; } void check_pval_item(pval *item, struct argapp *apps, int in_globals) { pval *lp; #ifdef AAL_ARGCHECK struct argapp *app, *found; #endif struct pval *macro_def; struct pval *app_def; char errmsg[4096]; char *strp; switch (item->type) { case PV_WORD: /* fields: item->u1.str == string associated with this (word). item->u2.arglist == pval list of 4 PV_WORD elements for time values (only in PV_INCLUDES) */ break; case PV_MACRO: /* fields: item->u1.str == name of macro item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user item->u2.arglist->u1.str == argument item->u2.arglist->next == next arg item->u3.macro_statements == pval list of statements in macro body. */ in_abstract_context = 0; current_context = item; current_extension = 0; for (lp=item->u2.arglist; lp; lp=lp->next) { } check_pval(item->u3.macro_statements, apps,in_globals); break; case PV_CONTEXT: /* fields: item->u1.str == name of context item->u2.statements == pval list of statements in context body item->u3.abstract == int 1 if an abstract keyword were present */ current_context = item; current_extension = 0; if ( item->u3.abstract ) { in_abstract_context = 1; check_abstract_reference(item); } else in_abstract_context = 0; check_pval(item->u2.statements, apps,in_globals); break; case PV_MACRO_CALL: /* fields: item->u1.str == name of macro to call item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user item->u2.arglist->u1.str == argument item->u2.arglist->next == next arg */ macro_def = find_macro(item->u1.str); if (!macro_def) { /* here is a good place to check to see if the definition is in extensions.conf! */ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: macro call to non-existent %s ! Hopefully it is present in extensions.conf! \n", item->filename, item->startline, item->endline, item->u1.str); warns++; } else if (macro_def->type != PV_MACRO) { ast_log(LOG_ERROR,"Error: file %s, line %d-%d: macro call to %s references a context, not a macro!\n", item->filename, item->startline, item->endline, item->u1.str); errs++; } else { /* macro_def is a MACRO, so do the args match in number? */ int hereargs = 0; int thereargs = 0; for (lp=item->u2.arglist; lp; lp=lp->next) { hereargs++; } for (lp=macro_def->u2.arglist; lp; lp=lp->next) { thereargs++; } if (hereargs != thereargs ) { ast_log(LOG_ERROR, "Error: file %s, line %d-%d: The macro call to %s has %d arguments, but the macro definition has %d arguments\n", item->filename, item->startline, item->endline, item->u1.str, hereargs, thereargs); errs++; } } break; case PV_APPLICATION_CALL: /* fields: item->u1.str == name of application to call item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user item->u2.arglist->u1.str == argument item->u2.arglist->next == next arg */ /* Need to check to see if the application is available! */ app_def = find_context(item->u1.str); if (app_def && app_def->type == PV_MACRO) { ast_log(LOG_ERROR,"Error: file %s, line %d-%d: application call to %s references an existing macro, but had no & preceding it!\n", item->filename, item->startline, item->endline, item->u1.str); errs++; } if (strcasecmp(item->u1.str,"GotoIf") == 0 || strcasecmp(item->u1.str,"GotoIfTime") == 0 || strcasecmp(item->u1.str,"while") == 0 || strcasecmp(item->u1.str,"endwhile") == 0 || strcasecmp(item->u1.str,"random") == 0 || strcasecmp(item->u1.str,"execIf") == 0 ) { ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: application call to %s needs to be re-written using AEL if, while, goto, etc. keywords instead!\n", item->filename, item->startline, item->endline, item->u1.str); warns++; } #ifdef AAL_ARGCHECK found = 0; for (app=apps; app; app=app->next) { if (strcasecmp(app->name, item->u1.str) == 0) { found =app; break; } } if (!found) { ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: application call to %s not listed in applist database!\n", item->filename, item->startline, item->endline, item->u1.str); warns++; } else check_app_args(item, item->u2.arglist, app); #endif break; case PV_CASE: /* fields: item->u1.str == value of case item->u2.statements == pval list of statements under the case */ /* Make sure sequence of statements under case is terminated with goto, return, or break */ /* find the last statement */ check_pval(item->u2.statements, apps,in_globals); break; case PV_PATTERN: /* fields: item->u1.str == value of case item->u2.statements == pval list of statements under the case */ /* Make sure sequence of statements under case is terminated with goto, return, or break */ /* find the last statement */ check_pval(item->u2.statements, apps,in_globals); break; case PV_DEFAULT: /* fields: item->u2.statements == pval list of statements under the case */ check_pval(item->u2.statements, apps,in_globals); break; case PV_CATCH: /* fields: item->u1.str == name of extension to catch item->u2.statements == pval list of statements in context body */ check_pval(item->u2.statements, apps,in_globals); break; case PV_SWITCHES: /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list */ check_pval(item->u1.list, apps,in_globals); break; case PV_ESWITCHES: /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list */ check_pval(item->u1.list, apps,in_globals); break; case PV_INCLUDES: /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list */ check_pval(item->u1.list, apps,in_globals); check_includes(item); for (lp=item->u1.list; lp; lp=lp->next){ char *incl_context = lp->u1.str; struct pval *that_context = find_context(incl_context); if ( lp->u2.arglist ) { check_timerange(lp->u2.arglist); check_dow(lp->u2.arglist->next); check_day(lp->u2.arglist->next->next); check_month(lp->u2.arglist->next->next->next); } if (that_context) { find_pval_gotos(that_context->u2.statements,0); } } break; case PV_STATEMENTBLOCK: /* fields: item->u1.list == pval list of statements in block, one per entry in the list */ check_pval(item->u1.list, apps,in_globals); break; case PV_VARDEC: /* fields: item->u1.str == variable name item->u2.val == variable value to assign */ /* the RHS of a vardec is encapsulated in a $[] expr. Is it legal? */ if( !in_globals ) { /* don't check stuff inside the globals context; no wrapping in $[ ] there... */ snprintf(errmsg,sizeof(errmsg), "file %s, line %d, columns %d-%d, variable declaration expr '%s':", config, item->startline, item->startcol, item->endcol, item->u2.val); ast_expr_register_extra_error_info(errmsg); ast_expr(item->u2.val, expr_output, sizeof(expr_output)); ast_expr_clear_extra_error_info(); if ( strpbrk(item->u2.val,"~!-+<>=*/&^") && !strstr(item->u2.val,"${") ) { ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n", item->filename, item->startline, item->endline, item->u2.val); warns++; } check_expr2_input(item,item->u2.val); } break; case PV_GOTO: /* fields: item->u1.list == pval list of PV_WORD target names, up to 3, in order as given by user. item->u1.list->u1.str == where the data on a PV_WORD will always be. */ /* don't check goto's in abstract contexts */ if ( in_abstract_context ) break; check_goto(item); break; case PV_LABEL: /* fields: item->u1.str == label name */ if ( strspn(item->u1.str, "0123456789") == strlen(item->u1.str) ) { ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: label '%s' is numeric, this is bad practice!\n", item->filename, item->startline, item->endline, item->u1.str); warns++; } check_label(item); break; case PV_FOR: /* fields: item->u1.for_init == a string containing the initalizer item->u2.for_test == a string containing the loop test item->u3.for_inc == a string containing the loop increment item->u4.for_statements == a pval list of statements in the for () */ snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, for test expr '%s':", config, item->startline, item->startcol, item->endcol, item->u2.for_test); ast_expr_register_extra_error_info(errmsg); strp = strchr(item->u1.for_init, '='); if (strp) { ast_expr(strp+1, expr_output, sizeof(expr_output)); } ast_expr(item->u2.for_test, expr_output, sizeof(expr_output)); strp = strchr(item->u3.for_inc, '='); if (strp) { ast_expr(strp+1, expr_output, sizeof(expr_output)); } if ( strpbrk(item->u2.for_test,"~!-+<>=*/&^") && !strstr(item->u2.for_test,"${") ) { ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n", item->filename, item->startline, item->endline, item->u2.for_test); warns++; } if ( strpbrk(item->u3.for_inc,"~!-+<>=*/&^") && !strstr(item->u3.for_inc,"${") ) { ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n", item->filename, item->startline, item->endline, item->u3.for_inc); warns++; } check_expr2_input(item,item->u2.for_test); check_expr2_input(item,item->u3.for_inc); ast_expr_clear_extra_error_info(); check_pval(item->u4.for_statements, apps,in_globals); break; case PV_WHILE: /* fields: item->u1.str == the while conditional, as supplied by user item->u2.statements == a pval list of statements in the while () */ snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, while expr '%s':", config, item->startline, item->startcol, item->endcol, item->u1.str); ast_expr_register_extra_error_info(errmsg); ast_expr(item->u1.str, expr_output, sizeof(expr_output)); ast_expr_clear_extra_error_info(); if ( strpbrk(item->u1.str,"~!-+<>=*/&^") && !strstr(item->u1.str,"${") ) { ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n", item->filename, item->startline, item->endline, item->u1.str); warns++; } check_expr2_input(item,item->u1.str); check_pval(item->u2.statements, apps,in_globals); break; case PV_BREAK: /* fields: none */ check_break(item); break; case PV_RETURN: /* fields: none */ break; case PV_CONTINUE: /* fields: none */ check_continue(item); break; case PV_RANDOM: /* fields: item->u1.str == the random number expression, as supplied by user item->u2.statements == a pval list of statements in the if () item->u3.else_statements == a pval list of statements in the else (could be zero) */ snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, random expr '%s':", config, item->startline, item->startcol, item->endcol, item->u1.str); ast_expr_register_extra_error_info(errmsg); ast_expr(item->u1.str, expr_output, sizeof(expr_output)); ast_expr_clear_extra_error_info(); if ( strpbrk(item->u1.str,"~!-+<>=*/&^") && !strstr(item->u1.str,"${") ) { ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: random expression '%s' has operators, but no variables. Interesting...\n", item->filename, item->startline, item->endline, item->u1.str); warns++; } check_expr2_input(item,item->u1.str); check_pval(item->u2.statements, apps,in_globals); if (item->u3.else_statements) { check_pval(item->u3.else_statements, apps,in_globals); } break; case PV_IFTIME: /* fields: item->u1.list == the if time values, 4 of them, each in PV_WORD, linked list item->u2.statements == a pval list of statements in the if () item->u3.else_statements == a pval list of statements in the else (could be zero) */ if ( item->u2.arglist ) { check_timerange(item->u1.list); check_dow(item->u1.list->next); check_day(item->u1.list->next->next); check_month(item->u1.list->next->next->next); } check_pval(item->u2.statements, apps,in_globals); if (item->u3.else_statements) { check_pval(item->u3.else_statements, apps,in_globals); } break; case PV_IF: /* fields: item->u1.str == the if conditional, as supplied by user item->u2.statements == a pval list of statements in the if () item->u3.else_statements == a pval list of statements in the else (could be zero) */ snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, if expr '%s':", config, item->startline, item->startcol, item->endcol, item->u1.str); ast_expr_register_extra_error_info(errmsg); ast_expr(item->u1.str, expr_output, sizeof(expr_output)); ast_expr_clear_extra_error_info(); if ( strpbrk(item->u1.str,"~!-+<>=*/&^") && !strstr(item->u1.str,"${") ) { ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression '%s' has operators, but no variables. Interesting...\n", item->filename, item->startline, item->endline, item->u1.str); warns++; } check_expr2_input(item,item->u1.str); check_pval(item->u2.statements, apps,in_globals); if (item->u3.else_statements) { check_pval(item->u3.else_statements, apps,in_globals); } break; case PV_SWITCH: /* fields: item->u1.str == the switch expression item->u2.statements == a pval list of statements in the switch, (will be case statements, most likely!) */ /* we can check the switch expression, see if it matches any of the app variables... if it does, then, are all the possible cases accounted for? */ check_switch_expr(item, apps); check_pval(item->u2.statements, apps,in_globals); break; case PV_EXTENSION: /* fields: item->u1.str == the extension name, label, whatever it's called item->u2.statements == a pval list of statements in the extension item->u3.hints == a char * hint argument item->u4.regexten == an int boolean. non-zero says that regexten was specified */ current_extension = item ; check_pval(item->u2.statements, apps,in_globals); break; case PV_IGNOREPAT: /* fields: item->u1.str == the ignorepat data */ break; case PV_GLOBALS: /* fields: item->u1.statements == pval list of statements, usually vardecs */ in_abstract_context = 0; check_pval(item->u1.statements, apps, 1); break; default: break; } } void check_pval(pval *item, struct argapp *apps, int in_globals) { pval *i; /* checks to do: 1. Do goto's point to actual labels? 2. Do macro calls reference a macro? 3. Does the number of macro args match the definition? 4. Is a macro call missing its & at the front? 5. Application calls-- we could check syntax for existing applications, but I need some some sort of universal description bnf for a general sort of method for checking arguments, in number, maybe even type, at least. Don't want to hand code checks for hundreds of applications. */ for (i=item; i; i=i->next) { check_pval_item(i,apps,in_globals); } } static void ael2_semantic_check(pval *item, int *arg_errs, int *arg_warns, int *arg_notes) { #ifdef AAL_ARGCHECK int argapp_errs =0; char *rfilename; #endif struct argapp *apps=0; if (!item) return; /* don't check an empty tree */ #ifdef AAL_ARGCHECK rfilename = alloca(10 + strlen(ast_config_AST_VAR_DIR)); sprintf(rfilename, "%s/applist", ast_config_AST_VAR_DIR); apps = argdesc_parse(rfilename, &argapp_errs); /* giveth */ #endif current_db = item; errs = warns = notes = 0; check_context_names(); check_pval(item, apps, 0); #ifdef AAL_ARGCHECK argdesc_destroy(apps); /* taketh away */ #endif current_db = 0; *arg_errs = errs; *arg_warns = warns; *arg_notes = notes; } /* =============================================================================================== */ /* "CODE" GENERATOR -- Convert the AEL representation to asterisk extension language */ /* =============================================================================================== */ static int control_statement_count = 0; struct ael_priority *new_prio(void) { struct ael_priority *x = (struct ael_priority *)calloc(sizeof(struct ael_priority),1); return x; } struct ael_extension *new_exten(void) { struct ael_extension *x = (struct ael_extension *)calloc(sizeof(struct ael_extension),1); return x; } void linkprio(struct ael_extension *exten, struct ael_priority *prio, struct ael_extension *mother_exten) { char *p1, *p2; if (!exten->plist) { exten->plist = prio; exten->plist_last = prio; } else { exten->plist_last->next = prio; exten->plist_last = prio; } if( !prio->exten ) prio->exten = exten; /* don't override the switch value */ /* The following code will cause all priorities within an extension to have ${EXTEN} or ${EXTEN: replaced with ~~EXTEN~~, which is set just before the first switch in an exten. The switches will muck up the original ${EXTEN} value, so we save it away and the user accesses this copy instead. */ if (prio->appargs && ((mother_exten && mother_exten->has_switch) || exten->has_switch) ) { while ((p1 = strstr(prio->appargs, "${EXTEN}"))) { p2 = malloc(strlen(prio->appargs)+5); *p1 = 0; strcpy(p2, prio->appargs); strcat(p2, "${~~EXTEN~~}"); if (*(p1+8)) strcat(p2, p1+8); free(prio->appargs); prio->appargs = p2; } while ((p1 = strstr(prio->appargs, "${EXTEN:"))) { p2 = malloc(strlen(prio->appargs)+5); *p1 = 0; strcpy(p2, prio->appargs); strcat(p2, "${~~EXTEN~~:"); if (*(p1+8)) strcat(p2, p1+8); free(prio->appargs); prio->appargs = p2; } } } void destroy_extensions(struct ael_extension *exten) { struct ael_extension *ne, *nen; for (ne=exten; ne; ne=nen) { struct ael_priority *pe, *pen; if (ne->name) free(ne->name); /* cidmatch fields are allocated with name, and freed when the name field is freed. Don't do a free for this field, unless you LIKE to see a crash! */ if (ne->hints) free(ne->hints); for (pe=ne->plist; pe; pe=pen) { pen = pe->next; if (pe->app) free(pe->app); pe->app = 0; if (pe->appargs) free(pe->appargs); pe->appargs = 0; pe->origin = 0; pe->goto_true = 0; pe->goto_false = 0; free(pe); } nen = ne->next_exten; ne->next_exten = 0; ne->plist =0; ne->plist_last = 0; ne->next_exten = 0; ne->loop_break = 0; ne->loop_continue = 0; free(ne); } } static int label_inside_case(pval *label) { pval *p = label; while( p && p->type != PV_MACRO && p->type != PV_CONTEXT ) /* early cutout, sort of */ { if( p->type == PV_CASE || p->type == PV_DEFAULT || p->type == PV_PATTERN ) { return 1; } p = p->dad; } return 0; } static void linkexten(struct ael_extension *exten, struct ael_extension *add) { add->next_exten = exten->next_exten; /* this will reverse the order. Big deal. */ exten->next_exten = add; } static void remove_spaces_before_equals(char *str) { char *p; while( str && *str && *str != '=' ) { if( *str == ' ' || *str == '\n' || *str == '\r' || *str == '\t' ) { p = str; while( *p ) { *p = *(p+1); p++; } } else str++; } } static void gen_match_to_pattern(char *pattern, char *result) { /* the result will be a string that will be matched by pattern */ char *p=pattern, *t=result; while (*p) { if (*p == 'x' || *p == 'n' || *p == 'z' || *p == 'X' || *p == 'N' || *p == 'Z') *t++ = '9'; else if (*p == '[') { char *z = p+1; while (*z != ']') z++; if (*(z+1)== ']') z++; *t++=*(p+1); /* use the first char in the set */ p = z; } else { *t++ = *p; } p++; } *t++ = 0; /* cap it off */ } static void gen_prios(struct ael_extension *exten, char *label, pval *statement, struct ael_extension *mother_exten, struct ast_context *this_context ) { pval *p,*p2,*p3; struct ael_priority *pr; struct ael_priority *for_init, *for_test, *for_inc, *for_loop, *for_end; struct ael_priority *while_test, *while_loop, *while_end; struct ael_priority *switch_set, *switch_test, *switch_end, *fall_thru, *switch_empty; struct ael_priority *if_test, *if_end, *if_skip, *if_false; #ifdef OLD_RAND_ACTION struct ael_priority *rand_test, *rand_end, *rand_skip; #endif char buf1[2000]; char buf2[2000]; char *strp, *strp2; char new_label[2000]; int default_exists; int local_control_statement_count; struct ael_priority *loop_break_save; struct ael_priority *loop_continue_save; struct ael_extension *switch_case,*switch_null; for (p=statement; p; p=p->next) { switch (p->type) { case PV_VARDEC: pr = new_prio(); pr->type = AEL_APPCALL; snprintf(buf1,sizeof(buf1),"%s=$[%s]", p->u1.str, p->u2.val); pr->app = strdup("Set"); remove_spaces_before_equals(buf1); pr->appargs = strdup(buf1); pr->origin = p; linkprio(exten, pr, mother_exten); break; case PV_GOTO: pr = new_prio(); pr->type = AEL_APPCALL; p->u2.goto_target = get_goto_target(p); if( p->u2.goto_target ) { p->u3.goto_target_in_case = p->u2.goto_target->u2.label_in_case = label_inside_case(p->u2.goto_target); } if (!p->u1.list->next) /* just one */ { pr->app = strdup("Goto"); if (!mother_exten) pr->appargs = strdup(p->u1.list->u1.str); else { /* for the case of simple within-extension gotos in case/pattern/default statement blocks: */ snprintf(buf1,sizeof(buf1),"%s|%s", mother_exten->name, p->u1.list->u1.str); pr->appargs = strdup(buf1); } } else if (p->u1.list->next && !p->u1.list->next->next) /* two */ { snprintf(buf1,sizeof(buf1),"%s|%s", p->u1.list->u1.str, p->u1.list->next->u1.str); pr->app = strdup("Goto"); pr->appargs = strdup(buf1); } else if (p->u1.list->next && p->u1.list->next->next) { snprintf(buf1,sizeof(buf1),"%s|%s|%s", p->u1.list->u1.str, p->u1.list->next->u1.str, p->u1.list->next->next->u1.str); pr->app = strdup("Goto"); pr->appargs = strdup(buf1); } pr->origin = p; linkprio(exten, pr, mother_exten); break; case PV_LABEL: pr = new_prio(); pr->type = AEL_LABEL; pr->origin = p; p->u3.compiled_label = exten; linkprio(exten, pr, mother_exten); break; case PV_FOR: control_statement_count++; loop_break_save = exten->loop_break; /* save them, then restore before leaving */ loop_continue_save = exten->loop_continue; snprintf(new_label,sizeof(new_label),"for-%s-%d", label, control_statement_count); for_init = new_prio(); for_inc = new_prio(); for_test = new_prio(); for_loop = new_prio(); for_end = new_prio(); for_init->type = AEL_APPCALL; for_inc->type = AEL_APPCALL; for_test->type = AEL_FOR_CONTROL; for_test->goto_false = for_end; for_loop->type = AEL_CONTROL1; /* simple goto */ for_end->type = AEL_APPCALL; for_init->app = strdup("Set"); strcpy(buf2,p->u1.for_init); remove_spaces_before_equals(buf2); strp = strchr(buf2, '='); if (strp) { strp2 = strchr(p->u1.for_init, '='); *(strp+1) = 0; strcat(buf2,"$["); strncat(buf2,strp2+1, sizeof(buf2)-strlen(strp2+1)-2); strcat(buf2,"]"); for_init->appargs = strdup(buf2); /* for_init->app = strdup("Set"); just set! */ } else { strp2 = p->u1.for_init; while (*strp2 && isspace(*strp2)) strp2++; if (*strp2 == '&') { /* itsa macro call */ char *strp3 = strp2+1; while (*strp3 && isspace(*strp3)) strp3++; strcpy(buf2, strp3); strp3 = strchr(buf2,'('); if (strp3) { *strp3 = '|'; } while ((strp3=strchr(buf2,','))) { *strp3 = '|'; } strp3 = strrchr(buf2, ')'); if (strp3) *strp3 = 0; /* remove the closing paren */ for_init->appargs = strdup(buf2); if (for_init->app) free(for_init->app); for_init->app = strdup("Macro"); } else { /* must be a regular app call */ char *strp3; strcpy(buf2, strp2); strp3 = strchr(buf2,'('); if (strp3) { *strp3 = 0; if (for_init->app) free(for_init->app); for_init->app = strdup(buf2); for_init->appargs = strdup(strp3+1); strp3 = strrchr(for_init->appargs, ')'); if (strp3) *strp3 = 0; /* remove the closing paren */ } } } strcpy(buf2,p->u3.for_inc); remove_spaces_before_equals(buf2); strp = strchr(buf2, '='); if (strp) { /* there's an = in this part; that means an assignment. set it up */ strp2 = strchr(p->u3.for_inc, '='); *(strp+1) = 0; strcat(buf2,"$["); strncat(buf2,strp2+1, sizeof(buf2)-strlen(strp2+1)-2); strcat(buf2,"]"); for_inc->appargs = strdup(buf2); for_inc->app = strdup("Set"); } else { strp2 = p->u3.for_inc; while (*strp2 && isspace(*strp2)) strp2++; if (*strp2 == '&') { /* itsa macro call */ char *strp3 = strp2+1; while (*strp3 && isspace(*strp3)) strp3++; strcpy(buf2, strp3); strp3 = strchr(buf2,'('); if (strp3) { *strp3 = '|'; } while ((strp3=strchr(buf2,','))) { *strp3 = '|'; } strp3 = strrchr(buf2, ')'); if (strp3) *strp3 = 0; /* remove the closing paren */ for_inc->appargs = strdup(buf2); for_inc->app = strdup("Macro"); } else { /* must be a regular app call */ char *strp3; strcpy(buf2, strp2); strp3 = strchr(buf2,'('); if (strp3) { *strp3 = 0; for_inc->app = strdup(buf2); for_inc->appargs = strdup(strp3+1); strp3 = strrchr(for_inc->appargs, ')'); if (strp3) *strp3 = 0; /* remove the closing paren */ } } } snprintf(buf1,sizeof(buf1),"$[%s]",p->u2.for_test); for_test->app = 0; for_test->appargs = strdup(buf1); for_loop->goto_true = for_test; snprintf(buf1,sizeof(buf1),"Finish for-%s-%d", label, control_statement_count); for_end->app = strdup("NoOp"); for_end->appargs = strdup(buf1); /* link & load! */ linkprio(exten, for_init, mother_exten); linkprio(exten, for_test, mother_exten); /* now, put the body of the for loop here */ exten->loop_break = for_end; exten->loop_continue = for_inc; gen_prios(exten, new_label, p->u4.for_statements, mother_exten, this_context); /* this will link in all the statements here */ linkprio(exten, for_inc, mother_exten); linkprio(exten, for_loop, mother_exten); linkprio(exten, for_end, mother_exten); exten->loop_break = loop_break_save; exten->loop_continue = loop_continue_save; for_loop->origin = p; break; case PV_WHILE: control_statement_count++; loop_break_save = exten->loop_break; /* save them, then restore before leaving */ loop_continue_save = exten->loop_continue; snprintf(new_label,sizeof(new_label),"while-%s-%d", label, control_statement_count); while_test = new_prio(); while_loop = new_prio(); while_end = new_prio(); while_test->type = AEL_FOR_CONTROL; while_test->goto_false = while_end; while_loop->type = AEL_CONTROL1; /* simple goto */ while_end->type = AEL_APPCALL; snprintf(buf1,sizeof(buf1),"$[%s]",p->u1.str); while_test->app = 0; while_test->appargs = strdup(buf1); while_loop->goto_true = while_test; snprintf(buf1,sizeof(buf1),"Finish while-%s-%d", label, control_statement_count); while_end->app = strdup("NoOp"); while_end->appargs = strdup(buf1); linkprio(exten, while_test, mother_exten); /* now, put the body of the for loop here */ exten->loop_break = while_end; exten->loop_continue = while_test; gen_prios(exten, new_label, p->u2.statements, mother_exten, this_context); /* this will link in all the while body statements here */ linkprio(exten, while_loop, mother_exten); linkprio(exten, while_end, mother_exten); exten->loop_break = loop_break_save; exten->loop_continue = loop_continue_save; while_loop->origin = p; break; case PV_SWITCH: control_statement_count++; local_control_statement_count = control_statement_count; loop_break_save = exten->loop_break; /* save them, then restore before leaving */ loop_continue_save = exten->loop_continue; snprintf(new_label,sizeof(new_label),"sw-%s-%d", label, control_statement_count); if ((mother_exten && !mother_exten->has_switch)) { switch_set = new_prio(); switch_set->type = AEL_APPCALL; switch_set->app = strdup("Set"); switch_set->appargs = strdup("~~EXTEN~~=${EXTEN}"); linkprio(exten, switch_set, mother_exten); mother_exten->has_switch = 1; } else if ((exten && !exten->has_switch)) { switch_set = new_prio(); switch_set->type = AEL_APPCALL; switch_set->app = strdup("Set"); switch_set->appargs = strdup("~~EXTEN~~=${EXTEN}"); linkprio(exten, switch_set, exten); exten->has_switch = 1; } switch_test = new_prio(); switch_end = new_prio(); switch_test->type = AEL_APPCALL; switch_end->type = AEL_APPCALL; strncpy(buf2,p->u1.str,sizeof(buf2)); buf2[sizeof(buf2)-1] = 0; /* just in case */ substitute_commas(buf2); snprintf(buf1,sizeof(buf1),"sw-%d-%s|10",control_statement_count, buf2); switch_test->app = strdup("Goto"); switch_test->appargs = strdup(buf1); snprintf(buf1,sizeof(buf1),"Finish switch-%s-%d", label, control_statement_count); switch_end->app = strdup("NoOp"); switch_end->appargs = strdup(buf1); switch_end->origin = p; switch_end->exten = exten; linkprio(exten, switch_test, mother_exten); linkprio(exten, switch_end, mother_exten); exten->loop_break = switch_end; exten->loop_continue = 0; default_exists = 0; for (p2=p->u2.statements; p2; p2=p2->next) { /* now, for each case/default put the body of the for loop here */ if (p2->type == PV_CASE) { /* ok, generate a extension and link it in */ switch_case = new_exten(); switch_case->context = this_context; switch_case->is_switch = 1; /* the break/continue locations are inherited from parent */ switch_case->loop_break = exten->loop_break; switch_case->loop_continue = exten->loop_continue; linkexten(exten,switch_case); strncpy(buf2,p2->u1.str,sizeof(buf2)); buf2[sizeof(buf2)-1] = 0; /* just in case */ substitute_commas(buf2); snprintf(buf1,sizeof(buf1),"sw-%d-%s", local_control_statement_count, buf2); switch_case->name = strdup(buf1); snprintf(new_label,sizeof(new_label),"sw-%s-%s-%d", label, buf2, local_control_statement_count); gen_prios(switch_case, new_label, p2->u2.statements, exten, this_context); /* this will link in all the case body statements here */ /* here is where we write code to "fall thru" to the next case... if there is one... */ for (p3=p2->u2.statements; p3; p3=p3->next) { if (!p3->next) break; } /* p3 now points the last statement... */ if (!p3 || ( p3->type != PV_GOTO && p3->type != PV_BREAK && p3->type != PV_RETURN) ) { /* is there a following CASE/PATTERN/DEFAULT? */ if (p2->next && p2->next->type == PV_CASE) { fall_thru = new_prio(); fall_thru->type = AEL_APPCALL; fall_thru->app = strdup("Goto"); strncpy(buf2,p2->next->u1.str,sizeof(buf2)); buf2[sizeof(buf2)-1] = 0; /* just in case */ substitute_commas(buf2); snprintf(buf1,sizeof(buf1),"sw-%d-%s|10",local_control_statement_count, buf2); fall_thru->appargs = strdup(buf1); linkprio(switch_case, fall_thru, mother_exten); } else if (p2->next && p2->next->type == PV_PATTERN) { fall_thru = new_prio(); fall_thru->type = AEL_APPCALL; fall_thru->app = strdup("Goto"); gen_match_to_pattern(p2->next->u1.str, buf2); substitute_commas(buf2); snprintf(buf1,sizeof(buf1),"sw-%d-%s|10", local_control_statement_count, buf2); fall_thru->appargs = strdup(buf1); linkprio(switch_case, fall_thru, mother_exten); } else if (p2->next && p2->next->type == PV_DEFAULT) { fall_thru = new_prio(); fall_thru->type = AEL_APPCALL; fall_thru->app = strdup("Goto"); snprintf(buf1,sizeof(buf1),"sw-%d-.|10",local_control_statement_count); fall_thru->appargs = strdup(buf1); linkprio(switch_case, fall_thru, mother_exten); } else if (!p2->next) { fall_thru = new_prio(); fall_thru->type = AEL_CONTROL1; fall_thru->goto_true = switch_end; fall_thru->app = strdup("Goto"); linkprio(switch_case, fall_thru, mother_exten); } } if (switch_case->return_needed) { char buf[2000]; struct ael_priority *np2 = new_prio(); np2->type = AEL_APPCALL; np2->app = strdup("NoOp"); snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name); np2->appargs = strdup(buf); linkprio(switch_case, np2, mother_exten); switch_case-> return_target = np2; } } else if (p2->type == PV_PATTERN) { /* ok, generate a extension and link it in */ switch_case = new_exten(); switch_case->context = this_context; switch_case->is_switch = 1; /* the break/continue locations are inherited from parent */ switch_case->loop_break = exten->loop_break; switch_case->loop_continue = exten->loop_continue; linkexten(exten,switch_case); strncpy(buf2,p2->u1.str,sizeof(buf2)); buf2[sizeof(buf2)-1] = 0; /* just in case */ substitute_commas(buf2); snprintf(buf1,sizeof(buf1),"_sw-%d-%s", local_control_statement_count, buf2); switch_case->name = strdup(buf1); snprintf(new_label,sizeof(new_label),"sw-%s-%s-%d", label, buf2, local_control_statement_count); gen_prios(switch_case, new_label, p2->u2.statements, exten, this_context); /* this will link in all the while body statements here */ /* here is where we write code to "fall thru" to the next case... if there is one... */ for (p3=p2->u2.statements; p3; p3=p3->next) { if (!p3->next) break; } /* p3 now points the last statement... */ if (!p3 || ( p3->type != PV_GOTO && p3->type != PV_BREAK && p3->type != PV_RETURN)) { /* is there a following CASE/PATTERN/DEFAULT? */ if (p2->next && p2->next->type == PV_CASE) { fall_thru = new_prio(); fall_thru->type = AEL_APPCALL; fall_thru->app = strdup("Goto"); strncpy(buf2,p2->next->u1.str,sizeof(buf2)); buf2[sizeof(buf2)-1] = 0; /* just in case */ substitute_commas(buf2); snprintf(buf1,sizeof(buf1),"sw-%d-%s|10",local_control_statement_count, buf2); fall_thru->appargs = strdup(buf1); linkprio(switch_case, fall_thru, mother_exten); } else if (p2->next && p2->next->type == PV_PATTERN) { fall_thru = new_prio(); fall_thru->type = AEL_APPCALL; fall_thru->app = strdup("Goto"); gen_match_to_pattern(p2->next->u1.str, buf2); substitute_commas(buf2); snprintf(buf1,sizeof(buf1),"sw-%d-%s|10",local_control_statement_count, buf2); fall_thru->appargs = strdup(buf1); linkprio(switch_case, fall_thru, mother_exten); } else if (p2->next && p2->next->type == PV_DEFAULT) { fall_thru = new_prio(); fall_thru->type = AEL_APPCALL; fall_thru->app = strdup("Goto"); snprintf(buf1,sizeof(buf1),"sw-%d-.|10",local_control_statement_count); fall_thru->appargs = strdup(buf1); linkprio(switch_case, fall_thru, mother_exten); } else if (!p2->next) { fall_thru = new_prio(); fall_thru->type = AEL_CONTROL1; fall_thru->goto_true = switch_end; fall_thru->app = strdup("Goto"); linkprio(switch_case, fall_thru, mother_exten); } } if (switch_case->return_needed) { char buf[2000]; struct ael_priority *np2 = new_prio(); np2->type = AEL_APPCALL; np2->app = strdup("NoOp"); snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name); np2->appargs = strdup(buf); linkprio(switch_case, np2, mother_exten); switch_case-> return_target = np2; } } else if (p2->type == PV_DEFAULT) { /* ok, generate a extension and link it in */ switch_case = new_exten(); switch_case->context = this_context; switch_case->is_switch = 1; /* new: the default case intros a pattern with ., which covers ALMOST everything. but it doesn't cover a NULL pattern. So, we'll define a null extension to match that goto's the default extension. */ default_exists++; switch_null = new_exten(); switch_null->context = this_context; switch_null->is_switch = 1; switch_empty = new_prio(); snprintf(buf1,sizeof(buf1),"sw-%d-.|10",local_control_statement_count); switch_empty->app = strdup("Goto"); switch_empty->appargs = strdup(buf1); linkprio(switch_null, switch_empty, mother_exten); snprintf(buf1,sizeof(buf1),"sw-%d-", local_control_statement_count); switch_null->name = strdup(buf1); switch_null->loop_break = exten->loop_break; switch_null->loop_continue = exten->loop_continue; linkexten(exten,switch_null); /* the break/continue locations are inherited from parent */ switch_case->loop_break = exten->loop_break; switch_case->loop_continue = exten->loop_continue; linkexten(exten,switch_case); snprintf(buf1,sizeof(buf1),"_sw-%d-.", local_control_statement_count); switch_case->name = strdup(buf1); snprintf(new_label,sizeof(new_label),"sw-%s-default-%d", label, local_control_statement_count); gen_prios(switch_case, new_label, p2->u2.statements, exten, this_context); /* this will link in all the default: body statements here */ /* here is where we write code to "fall thru" to the next case... if there is one... */ for (p3=p2->u2.statements; p3; p3=p3->next) { if (!p3->next) break; } /* p3 now points the last statement... */ if (!p3 || (p3->type != PV_GOTO && p3->type != PV_BREAK && p3->type != PV_RETURN)) { /* is there a following CASE/PATTERN/DEFAULT? */ if (p2->next && p2->next->type == PV_CASE) { fall_thru = new_prio(); fall_thru->type = AEL_APPCALL; fall_thru->app = strdup("Goto"); strncpy(buf2,p2->next->u1.str,sizeof(buf2)); buf2[sizeof(buf2)-1] = 0; /* just in case */ substitute_commas(buf2); snprintf(buf1,sizeof(buf1),"sw-%d-%s|10",local_control_statement_count, buf2); fall_thru->appargs = strdup(buf1); linkprio(switch_case, fall_thru, mother_exten); } else if (p2->next && p2->next->type == PV_PATTERN) { fall_thru = new_prio(); fall_thru->type = AEL_APPCALL; fall_thru->app = strdup("Goto"); gen_match_to_pattern(p2->next->u1.str, buf2); substitute_commas(buf2); snprintf(buf1,sizeof(buf1),"sw-%d-%s|10",local_control_statement_count, buf2); fall_thru->appargs = strdup(buf1); linkprio(switch_case, fall_thru, mother_exten); } else if (p2->next && p2->next->type == PV_DEFAULT) { fall_thru = new_prio(); fall_thru->type = AEL_APPCALL; fall_thru->app = strdup("Goto"); snprintf(buf1,sizeof(buf1),"sw-%d-.|10",local_control_statement_count); fall_thru->appargs = strdup(buf1); linkprio(switch_case, fall_thru, mother_exten); } else if (!p2->next) { fall_thru = new_prio(); fall_thru->type = AEL_CONTROL1; fall_thru->goto_true = switch_end; fall_thru->app = strdup("Goto"); linkprio(switch_case, fall_thru, mother_exten); } } if (switch_case->return_needed) { char buf[2000]; struct ael_priority *np2 = new_prio(); np2->type = AEL_APPCALL; np2->app = strdup("NoOp"); snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name); np2->appargs = strdup(buf); linkprio(switch_case, np2, mother_exten); switch_case-> return_target = np2; } } else { /* what could it be??? */ } } exten->loop_break = loop_break_save; exten->loop_continue = loop_continue_save; switch_test->origin = p; switch_end->origin = p; break; case PV_MACRO_CALL: pr = new_prio(); pr->type = AEL_APPCALL; snprintf(buf1,sizeof(buf1),"%s", p->u1.str); for (p2 = p->u2.arglist; p2; p2 = p2->next) { strcat(buf1,"|"); strcat(buf1,p2->u1.str); } pr->app = strdup("Macro"); pr->appargs = strdup(buf1); pr->origin = p; linkprio(exten, pr, mother_exten); break; case PV_APPLICATION_CALL: pr = new_prio(); pr->type = AEL_APPCALL; buf1[0] = 0; for (p2 = p->u2.arglist; p2; p2 = p2->next) { if (p2 != p->u2.arglist ) strcat(buf1,"|"); substitute_commas(p2->u1.str); strcat(buf1,p2->u1.str); } pr->app = strdup(p->u1.str); pr->appargs = strdup(buf1); pr->origin = p; linkprio(exten, pr, mother_exten); break; case PV_BREAK: pr = new_prio(); pr->type = AEL_CONTROL1; /* simple goto */ pr->goto_true = exten->loop_break; pr->origin = p; linkprio(exten, pr, mother_exten); break; case PV_RETURN: /* hmmmm */ pr = new_prio(); pr->type = AEL_RETURN; /* simple goto */ exten->return_needed++; pr->app = strdup("Goto"); pr->appargs = strdup(""); pr->origin = p; linkprio(exten, pr, mother_exten); break; case PV_CONTINUE: pr = new_prio(); pr->type = AEL_CONTROL1; /* simple goto */ pr->goto_true = exten->loop_continue; pr->origin = p; linkprio(exten, pr, mother_exten); break; #ifdef OLD_RAND_ACTION case PV_RANDOM: control_statement_count++; snprintf(new_label,sizeof(new_label),"rand-%s-%d", label, control_statement_count); rand_test = new_prio(); rand_test->type = AEL_RAND_CONTROL; snprintf(buf1,sizeof(buf1),"$[%s]", p->u1.str ); rand_test->app = 0; rand_test->appargs = strdup(buf1); rand_test->origin = p; rand_end = new_prio(); rand_end->type = AEL_APPCALL; snprintf(buf1,sizeof(buf1),"Finish rand-%s-%d", label, control_statement_count); rand_end->app = strdup("NoOp"); rand_end->appargs = strdup(buf1); rand_skip = new_prio(); rand_skip->type = AEL_CONTROL1; /* simple goto */ rand_skip->goto_true = rand_end; rand_skip->origin = p; rand_test->goto_true = rand_skip; /* +1, really */ linkprio(exten, rand_test, mother_exten); if (p->u3.else_statements) { gen_prios(exten, new_label, p->u3.else_statements, mother_exten, this_context); /* this will link in all the else statements here */ } linkprio(exten, rand_skip, mother_exten); gen_prios(exten, new_label, p->u2.statements, mother_exten, this_context); /* this will link in all the "true" statements here */ linkprio(exten, rand_end, mother_exten); break; #endif case PV_IFTIME: control_statement_count++; snprintf(new_label,sizeof(new_label),"iftime-%s-%d", label, control_statement_count); if_test = new_prio(); if_test->type = AEL_IFTIME_CONTROL; snprintf(buf1,sizeof(buf1),"%s|%s|%s|%s", p->u1.list->u1.str, p->u1.list->next->u1.str, p->u1.list->next->next->u1.str, p->u1.list->next->next->next->u1.str); if_test->app = 0; if_test->appargs = strdup(buf1); if_test->origin = p; if_end = new_prio(); if_end->type = AEL_APPCALL; snprintf(buf1,sizeof(buf1),"Finish iftime-%s-%d", label, control_statement_count); if_end->app = strdup("NoOp"); if_end->appargs = strdup(buf1); if (p->u3.else_statements) { if_skip = new_prio(); if_skip->type = AEL_CONTROL1; /* simple goto */ if_skip->goto_true = if_end; if_skip->origin = p; } else { if_skip = 0; if_test->goto_false = if_end; } if_false = new_prio(); if_false->type = AEL_CONTROL1; if (p->u3.else_statements) { if_false->goto_true = if_skip; /* +1 */ } else { if_false->goto_true = if_end; } /* link & load! */ linkprio(exten, if_test, mother_exten); linkprio(exten, if_false, mother_exten); /* now, put the body of the if here */ gen_prios(exten, new_label, p->u2.statements, mother_exten, this_context); /* this will link in all the statements here */ if (p->u3.else_statements) { linkprio(exten, if_skip, mother_exten); gen_prios(exten, new_label, p->u3.else_statements, mother_exten, this_context); /* this will link in all the statements here */ } linkprio(exten, if_end, mother_exten); break; case PV_RANDOM: case PV_IF: control_statement_count++; snprintf(new_label,sizeof(new_label),"if-%s-%d", label, control_statement_count); if_test = new_prio(); if_end = new_prio(); if_test->type = AEL_IF_CONTROL; if_end->type = AEL_APPCALL; if ( p->type == PV_RANDOM ) snprintf(buf1,sizeof(buf1),"$[${RAND(0,99)} < (%s)]",p->u1.str); else snprintf(buf1,sizeof(buf1),"$[%s]",p->u1.str); if_test->app = 0; if_test->appargs = strdup(buf1); snprintf(buf1,sizeof(buf1),"Finish if-%s-%d", label, control_statement_count); if_end->app = strdup("NoOp"); if_end->appargs = strdup(buf1); if_test->origin = p; if (p->u3.else_statements) { if_skip = new_prio(); if_skip->type = AEL_CONTROL1; /* simple goto */ if_skip->goto_true = if_end; if_test->goto_false = if_skip;; } else { if_skip = 0; if_test->goto_false = if_end;; } /* link & load! */ linkprio(exten, if_test, mother_exten); /* now, put the body of the if here */ gen_prios(exten, new_label, p->u2.statements, mother_exten, this_context); /* this will link in all the statements here */ if (p->u3.else_statements) { linkprio(exten, if_skip, mother_exten); gen_prios(exten, new_label, p->u3.else_statements, mother_exten, this_context); /* this will link in all the statements here */ } linkprio(exten, if_end, mother_exten); break; case PV_STATEMENTBLOCK: gen_prios(exten, label, p->u1.list, mother_exten, this_context ); /* recurse into the block */ break; case PV_CATCH: control_statement_count++; /* generate an extension with name of catch, put all catch stats into this exten! */ switch_case = new_exten(); switch_case->context = this_context; linkexten(exten,switch_case); switch_case->name = strdup(p->u1.str); snprintf(new_label,sizeof(new_label),"catch-%s-%d",p->u1.str, control_statement_count); gen_prios(switch_case, new_label, p->u2.statements,mother_exten,this_context); /* this will link in all the catch body statements here */ if (switch_case->return_needed) { char buf[2000]; struct ael_priority *np2 = new_prio(); np2->type = AEL_APPCALL; np2->app = strdup("NoOp"); snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name); np2->appargs = strdup(buf); linkprio(switch_case, np2, mother_exten); switch_case-> return_target = np2; } break; default: break; } } } void set_priorities(struct ael_extension *exten) { int i; struct ael_priority *pr; do { if (exten->is_switch) i = 10; else if (exten->regexten) i=2; else i=1; for (pr=exten->plist; pr; pr=pr->next) { pr->priority_num = i; if (!pr->origin || (pr->origin && pr->origin->type != PV_LABEL) ) /* Labels don't show up in the dialplan, but we want them to point to the right priority, which would be the next line after the label; */ i++; } exten = exten->next_exten; } while ( exten ); } void add_extensions(struct ael_extension *exten) { struct ael_priority *pr; char *label=0; char realext[AST_MAX_EXTENSION]; if (!exten) { ast_log(LOG_WARNING, "This file is Empty!\n" ); return; } do { struct ael_priority *last = 0; memset(realext, '\0', sizeof(realext)); pbx_substitute_variables_helper(NULL, exten->name, realext, sizeof(realext) - 1); if (exten->hints) { if (ast_add_extension2(exten->context, 0 /*no replace*/, realext, PRIORITY_HINT, NULL, exten->cidmatch, exten->hints, NULL, ast_free, registrar)) { ast_log(LOG_WARNING, "Unable to add step at priority 'hint' of extension '%s'\n", exten->name); } } for (pr=exten->plist; pr; pr=pr->next) { char app[2000]; char appargs[2000]; /* before we can add the extension, we need to prep the app/appargs; the CONTROL types need to be done after the priority numbers are calculated. */ if (pr->type == AEL_LABEL) /* don't try to put labels in the dialplan! */ { last = pr; continue; } if (pr->app) strcpy(app, pr->app); else app[0] = 0; if (pr->appargs ) strcpy(appargs, pr->appargs); else appargs[0] = 0; switch( pr->type ) { case AEL_APPCALL: /* easy case. Everything is all set up */ break; case AEL_CONTROL1: /* FOR loop, WHILE loop, BREAK, CONTINUE, IF, IFTIME */ /* simple, unconditional goto. */ strcpy(app,"Goto"); if (pr->goto_true->origin && pr->goto_true->origin->type == PV_SWITCH ) { snprintf(appargs,sizeof(appargs),"%s|%d", pr->goto_true->exten->name, pr->goto_true->priority_num); } else if (pr->goto_true->origin && pr->goto_true->origin->type == PV_IFTIME && pr->goto_true->origin->u3.else_statements ) { snprintf(appargs,sizeof(appargs),"%d", pr->goto_true->priority_num+1); } else snprintf(appargs,sizeof(appargs),"%d", pr->goto_true->priority_num); break; case AEL_FOR_CONTROL: /* WHILE loop test, FOR loop test */ strcpy(app,"GotoIf"); snprintf(appargs,sizeof(appargs),"%s?%d:%d", pr->appargs, pr->priority_num+1, pr->goto_false->priority_num); break; case AEL_IF_CONTROL: strcpy(app,"GotoIf"); if (pr->origin->u3.else_statements ) snprintf(appargs,sizeof(appargs),"%s?%d:%d", pr->appargs, pr->priority_num+1, pr->goto_false->priority_num+1); else snprintf(appargs,sizeof(appargs),"%s?%d:%d", pr->appargs, pr->priority_num+1, pr->goto_false->priority_num); break; case AEL_RAND_CONTROL: strcpy(app,"Random"); snprintf(appargs,sizeof(appargs),"%s:%d", pr->appargs, pr->goto_true->priority_num+1); break; case AEL_IFTIME_CONTROL: strcpy(app,"GotoIfTime"); snprintf(appargs,sizeof(appargs),"%s?%d", pr->appargs, pr->priority_num+2); break; case AEL_RETURN: strcpy(app,"Goto"); snprintf(appargs,sizeof(appargs), "%d", exten->return_target->priority_num); break; default: break; } if (last && last->type == AEL_LABEL ) { label = last->origin->u1.str; } else label = 0; if (ast_add_extension2(exten->context, 0 /*no replace*/, realext, pr->priority_num, (label?label:NULL), exten->cidmatch, app, strdup(appargs), ast_free, registrar)) { ast_log(LOG_WARNING, "Unable to add step at priority '%d' of extension '%s'\n", pr->priority_num, exten->name); } last = pr; } exten = exten->next_exten; } while ( exten ); } static void attach_exten(struct ael_extension **list, struct ael_extension *newmem) { /* travel to the end of the list... */ struct ael_extension *lptr; if( !*list ) { *list = newmem; return; } lptr = *list; while( lptr->next_exten ) { lptr = lptr->next_exten; } /* lptr should now pointing to the last element in the list; it has a null next_exten pointer */ lptr->next_exten = newmem; } static pval *get_extension_or_contxt(pval *p) { while( p && p->type != PV_EXTENSION && p->type != PV_CONTEXT && p->type != PV_MACRO ) { p = p->dad; } return p; } static pval *get_contxt(pval *p) { while( p && p->type != PV_CONTEXT && p->type != PV_MACRO ) { p = p->dad; } return p; } static void fix_gotos_in_extensions(struct ael_extension *exten) { struct ael_extension *e; for(e=exten;e;e=e->next_exten) { struct ael_priority *p; for(p=e->plist;p;p=p->next) { if( p->origin && p->origin->type == PV_GOTO && p->origin->u3.goto_target_in_case ) { /* fix the extension of the goto target to the actual extension in the post-compiled dialplan */ pval *target = p->origin->u2.goto_target; struct ael_extension *z = target->u3.compiled_label; pval *pv2 = p->origin; char buf1[500]; char *apparg_save = p->appargs; p->appargs = 0; if (!pv2->u1.list->next) /* just one -- it won't hurt to repeat the extension */ { snprintf(buf1,sizeof(buf1),"%s|%s", z->name, pv2->u1.list->u1.str); p->appargs = strdup(buf1); } else if (pv2->u1.list->next && !pv2->u1.list->next->next) /* two */ { snprintf(buf1,sizeof(buf1),"%s|%s", z->name, pv2->u1.list->next->u1.str); p->appargs = strdup(buf1); } else if (pv2->u1.list->next && pv2->u1.list->next->next) { snprintf(buf1,sizeof(buf1),"%s|%s|%s", pv2->u1.list->u1.str, z->name, pv2->u1.list->next->next->u1.str); p->appargs = strdup(buf1); } else printf("WHAT? The goto doesn't fall into one of three cases for GOTO????\n"); if( apparg_save ) { free(apparg_save); } } } } } void ast_compile_ael2(struct ast_context **local_contexts, struct pval *root) { pval *p,*p2; struct ast_context *context; char buf[2000]; struct ael_extension *exten; struct ael_extension *exten_list = 0; for (p=root; p; p=p->next ) { /* do the globals first, so they'll be there when we try to eval them */ switch (p->type) { case PV_GLOBALS: /* just VARDEC elements */ for (p2=p->u1.list; p2; p2=p2->next) { char buf2[2000]; snprintf(buf2,sizeof(buf2),"%s=%s", p2->u1.str, p2->u2.val); pbx_builtin_setvar(NULL, buf2); } break; default: break; } } for (p=root; p; p=p->next ) { pval *lp; int argc; switch (p->type) { case PV_MACRO: strcpy(buf,"macro-"); strcat(buf,p->u1.str); context = ast_context_create(local_contexts, buf, registrar); exten = new_exten(); exten->context = context; exten->name = strdup("s"); argc = 1; for (lp=p->u2.arglist; lp; lp=lp->next) { /* for each arg, set up a "Set" command */ struct ael_priority *np2 = new_prio(); np2->type = AEL_APPCALL; np2->app = strdup("Set"); snprintf(buf,sizeof(buf),"%s=${ARG%d}", lp->u1.str, argc++); remove_spaces_before_equals(buf); np2->appargs = strdup(buf); linkprio(exten, np2, NULL); } /* add any includes */ for (p2=p->u3.macro_statements; p2; p2=p2->next) { pval *p3; switch (p2->type) { case PV_INCLUDES: for (p3 = p2->u1.list; p3 ;p3=p3->next) { if ( p3->u2.arglist ) { snprintf(buf,sizeof(buf), "%s|%s|%s|%s|%s", p3->u1.str, p3->u2.arglist->u1.str, p3->u2.arglist->next->u1.str, p3->u2.arglist->next->next->u1.str, p3->u2.arglist->next->next->next->u1.str); ast_context_add_include2(context, buf, registrar); } else ast_context_add_include2(context, p3->u1.str, registrar); } break; default: break; } } /* CONTAINS APPCALLS, CATCH, just like extensions... */ gen_prios(exten, p->u1.str, p->u3.macro_statements, 0, context ); if (exten->return_needed) { struct ael_priority *np2 = new_prio(); np2->type = AEL_APPCALL; np2->app = strdup("NoOp"); snprintf(buf,sizeof(buf),"End of Macro %s-%s",p->u1.str, exten->name); np2->appargs = strdup(buf); linkprio(exten, np2, NULL); exten-> return_target = np2; } set_priorities(exten); attach_exten(&exten_list, exten); break; case PV_GLOBALS: /* already done */ break; case PV_CONTEXT: context = ast_context_find_or_create(local_contexts, p->u1.str, registrar); /* contexts contain: ignorepat, includes, switches, eswitches, extensions, */ for (p2=p->u2.statements; p2; p2=p2->next) { pval *p3; char *s3; switch (p2->type) { case PV_EXTENSION: exten = new_exten(); exten->name = strdup(p2->u1.str); exten->context = context; if( (s3=strchr(exten->name, '/') ) != 0 ) { *s3 = 0; exten->cidmatch = s3+1; } if ( p2->u3.hints ) exten->hints = strdup(p2->u3.hints); exten->regexten = p2->u4.regexten; gen_prios(exten, p->u1.str, p2->u2.statements, 0, context ); if (exten->return_needed) { struct ael_priority *np2 = new_prio(); np2->type = AEL_APPCALL; np2->app = strdup("NoOp"); snprintf(buf,sizeof(buf),"End of Extension %s", exten->name); np2->appargs = strdup(buf); linkprio(exten, np2, NULL); exten-> return_target = np2; } /* is the last priority in the extension a label? Then add a trailing no-op */ if ( exten->plist_last && exten->plist_last->type == AEL_LABEL ) { struct ael_priority *np2 = new_prio(); np2->type = AEL_APPCALL; np2->app = strdup("NoOp"); snprintf(buf,sizeof(buf),"A NoOp to follow a trailing label %s", exten->plist_last->origin->u1.str); np2->appargs = strdup(buf); linkprio(exten, np2, NULL); } set_priorities(exten); attach_exten(&exten_list, exten); break; case PV_IGNOREPAT: ast_context_add_ignorepat2(context, p2->u1.str, registrar); break; case PV_INCLUDES: for (p3 = p2->u1.list; p3 ;p3=p3->next) { if ( p3->u2.arglist ) { snprintf(buf,sizeof(buf), "%s|%s|%s|%s|%s", p3->u1.str, p3->u2.arglist->u1.str, p3->u2.arglist->next->u1.str, p3->u2.arglist->next->next->u1.str, p3->u2.arglist->next->next->next->u1.str); ast_context_add_include2(context, buf, registrar); } else ast_context_add_include2(context, p3->u1.str, registrar); } break; case PV_SWITCHES: for (p3 = p2->u1.list; p3 ;p3=p3->next) { char *c = strchr(p3->u1.str, '/'); if (c) { *c = '\0'; c++; } else c = ""; ast_context_add_switch2(context, p3->u1.str, c, 0, registrar); } break; case PV_ESWITCHES: for (p3 = p2->u1.list; p3 ;p3=p3->next) { char *c = strchr(p3->u1.str, '/'); if (c) { *c = '\0'; c++; } else c = ""; ast_context_add_switch2(context, p3->u1.str, c, 1, registrar); } break; default: break; } } break; default: /* huh? what? */ break; } } /* moved these from being done after a macro or extension were processed, to after all processing is done, for the sake of fixing gotos to labels inside cases... */ /* I guess this would be considered 2nd pass of compiler now... */ fix_gotos_in_extensions(exten_list); /* find and fix extension ref in gotos to labels that are in case statements */ add_extensions(exten_list); /* actually makes calls to create priorities in ast_contexts -- feeds dialplan to asterisk */ destroy_extensions(exten_list); /* all that remains is an empty husk, discard of it as is proper */ } static int aeldebug = 0; /* interface stuff */ /* if all the below are static, who cares if they are present? */ static int pbx_load_module(void) { int errs=0, sem_err=0, sem_warn=0, sem_note=0; char *rfilename; struct ast_context *local_contexts=NULL, *con; struct pval *parse_tree; ast_log(LOG_NOTICE, "Starting AEL load process.\n"); if (config[0] == '/') rfilename = (char *)config; else { rfilename = alloca(strlen(config) + strlen(ast_config_AST_CONFIG_DIR) + 2); sprintf(rfilename, "%s/%s", ast_config_AST_CONFIG_DIR, config); } ast_log(LOG_NOTICE, "AEL load process: calculated config file name '%s'.\n", rfilename); if (access(rfilename,R_OK) != 0) { ast_log(LOG_NOTICE, "File %s not found; AEL declining load\n", rfilename); return AST_MODULE_LOAD_DECLINE; } parse_tree = ael2_parse(rfilename, &errs); ast_log(LOG_NOTICE, "AEL load process: parsed config file name '%s'.\n", rfilename); ael2_semantic_check(parse_tree, &sem_err, &sem_warn, &sem_note); if (errs == 0 && sem_err == 0) { ast_log(LOG_NOTICE, "AEL load process: checked config file name '%s'.\n", rfilename); ast_compile_ael2(&local_contexts, parse_tree); ast_log(LOG_NOTICE, "AEL load process: compiled config file name '%s'.\n", rfilename); ast_merge_contexts_and_delete(&local_contexts, registrar); ast_log(LOG_NOTICE, "AEL load process: merged config file name '%s'.\n", rfilename); for (con = ast_walk_contexts(NULL); con; con = ast_walk_contexts(con)) ast_context_verify_includes(con); ast_log(LOG_NOTICE, "AEL load process: verified config file name '%s'.\n", rfilename); } else { ast_log(LOG_ERROR, "Sorry, but %d syntax errors and %d semantic errors were detected. It doesn't make sense to compile.\n", errs, sem_err); destroy_pval(parse_tree); /* free up the memory */ return AST_MODULE_LOAD_DECLINE; } destroy_pval(parse_tree); /* free up the memory */ return AST_MODULE_LOAD_SUCCESS; } /* CLI interface */ static int ael2_debug_read(int fd, int argc, char *argv[]) { aeldebug |= DEBUG_READ; return 0; } static int ael2_debug_tokens(int fd, int argc, char *argv[]) { aeldebug |= DEBUG_TOKENS; return 0; } static int ael2_debug_macros(int fd, int argc, char *argv[]) { aeldebug |= DEBUG_MACROS; return 0; } static int ael2_debug_contexts(int fd, int argc, char *argv[]) { aeldebug |= DEBUG_CONTEXTS; return 0; } static int ael2_no_debug(int fd, int argc, char *argv[]) { aeldebug = 0; return 0; } static int ael2_reload(int fd, int argc, char *argv[]) { return (pbx_load_module()); } static struct ast_cli_entry cli_ael_no_debug = { { "ael", "no", "debug", NULL }, ael2_no_debug, NULL, NULL }; static struct ast_cli_entry cli_ael[] = { { { "ael", "reload", NULL }, ael2_reload, "Reload AEL configuration" }, { { "ael", "debug", "read", NULL }, ael2_debug_read, "Enable AEL read debug (does nothing)" }, { { "ael", "debug", "tokens", NULL }, ael2_debug_tokens, "Enable AEL tokens debug (does nothing)" }, { { "ael", "debug", "macros", NULL }, ael2_debug_macros, "Enable AEL macros debug (does nothing)" }, { { "ael", "debug", "contexts", NULL }, ael2_debug_contexts, "Enable AEL contexts debug (does nothing)" }, { { "ael", "nodebug", NULL }, ael2_no_debug, "Disable AEL debug messages", NULL, NULL, &cli_ael_no_debug }, }; static int unload_module(void) { ast_context_destroy(NULL, registrar); ast_cli_unregister_multiple(cli_ael, sizeof(cli_ael) / sizeof(struct ast_cli_entry)); return 0; } static int load_module(void) { ast_cli_register_multiple(cli_ael, sizeof(cli_ael) / sizeof(struct ast_cli_entry)); return (pbx_load_module()); } static int reload(void) { return pbx_load_module(); } #ifdef STANDALONE_AEL #define AST_MODULE "ael" int ael_external_load_module(void); int ael_external_load_module(void) { pbx_load_module(); return 1; } #endif AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Asterisk Extension Language Compiler", .load = load_module, .unload = unload_module, .reload = reload, ); /* DESTROY the PVAL tree ============================================================================ */ void destroy_pval_item(pval *item) { if (item == NULL) { ast_log(LOG_WARNING, "null item\n"); return; } if (item->filename) free(item->filename); switch (item->type) { case PV_WORD: /* fields: item->u1.str == string associated with this (word). */ if (item->u1.str ) free(item->u1.str); if ( item->u2.arglist ) destroy_pval(item->u2.arglist); break; case PV_MACRO: /* fields: item->u1.str == name of macro item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user item->u2.arglist->u1.str == argument item->u2.arglist->next == next arg item->u3.macro_statements == pval list of statements in macro body. */ destroy_pval(item->u2.arglist); if (item->u1.str ) free(item->u1.str); destroy_pval(item->u3.macro_statements); break; case PV_CONTEXT: /* fields: item->u1.str == name of context item->u2.statements == pval list of statements in context body item->u3.abstract == int 1 if an abstract keyword were present */ if (item->u1.str) free(item->u1.str); destroy_pval(item->u2.statements); break; case PV_MACRO_CALL: /* fields: item->u1.str == name of macro to call item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user item->u2.arglist->u1.str == argument item->u2.arglist->next == next arg */ if (item->u1.str) free(item->u1.str); destroy_pval(item->u2.arglist); break; case PV_APPLICATION_CALL: /* fields: item->u1.str == name of application to call item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user item->u2.arglist->u1.str == argument item->u2.arglist->next == next arg */ if (item->u1.str) free(item->u1.str); destroy_pval(item->u2.arglist); break; case PV_CASE: /* fields: item->u1.str == value of case item->u2.statements == pval list of statements under the case */ if (item->u1.str) free(item->u1.str); destroy_pval(item->u2.statements); break; case PV_PATTERN: /* fields: item->u1.str == value of case item->u2.statements == pval list of statements under the case */ if (item->u1.str) free(item->u1.str); destroy_pval(item->u2.statements); break; case PV_DEFAULT: /* fields: item->u2.statements == pval list of statements under the case */ destroy_pval(item->u2.statements); break; case PV_CATCH: /* fields: item->u1.str == name of extension to catch item->u2.statements == pval list of statements in context body */ if (item->u1.str) free(item->u1.str); destroy_pval(item->u2.statements); break; case PV_SWITCHES: /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list */ destroy_pval(item->u1.list); break; case PV_ESWITCHES: /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list */ destroy_pval(item->u1.list); break; case PV_INCLUDES: /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list item->u2.arglist == pval list of 4 PV_WORD elements for time values */ destroy_pval(item->u1.list); break; case PV_STATEMENTBLOCK: /* fields: item->u1.list == pval list of statements in block, one per entry in the list */ destroy_pval(item->u1.list); break; case PV_VARDEC: /* fields: item->u1.str == variable name item->u2.val == variable value to assign */ if (item->u1.str) free(item->u1.str); if (item->u2.val) free(item->u2.val); break; case PV_GOTO: /* fields: item->u1.list == pval list of PV_WORD target names, up to 3, in order as given by user. item->u1.list->u1.str == where the data on a PV_WORD will always be. */ destroy_pval(item->u1.list); break; case PV_LABEL: /* fields: item->u1.str == label name */ if (item->u1.str) free(item->u1.str); break; case PV_FOR: /* fields: item->u1.for_init == a string containing the initalizer item->u2.for_test == a string containing the loop test item->u3.for_inc == a string containing the loop increment item->u4.for_statements == a pval list of statements in the for () */ if (item->u1.for_init) free(item->u1.for_init); if (item->u2.for_test) free(item->u2.for_test); if (item->u3.for_inc) free(item->u3.for_inc); destroy_pval(item->u4.for_statements); break; case PV_WHILE: /* fields: item->u1.str == the while conditional, as supplied by user item->u2.statements == a pval list of statements in the while () */ if (item->u1.str) free(item->u1.str); destroy_pval(item->u2.statements); break; case PV_BREAK: /* fields: none */ break; case PV_RETURN: /* fields: none */ break; case PV_CONTINUE: /* fields: none */ break; case PV_IFTIME: /* fields: item->u1.list == the 4 time values, in PV_WORD structs, linked list item->u2.statements == a pval list of statements in the if () item->u3.else_statements == a pval list of statements in the else (could be zero) */ destroy_pval(item->u1.list); destroy_pval(item->u2.statements); if (item->u3.else_statements) { destroy_pval(item->u3.else_statements); } break; case PV_RANDOM: /* fields: item->u1.str == the random percentage, as supplied by user item->u2.statements == a pval list of statements in the true part () item->u3.else_statements == a pval list of statements in the else (could be zero) fall thru to If */ case PV_IF: /* fields: item->u1.str == the if conditional, as supplied by user item->u2.statements == a pval list of statements in the if () item->u3.else_statements == a pval list of statements in the else (could be zero) */ if (item->u1.str) free(item->u1.str); destroy_pval(item->u2.statements); if (item->u3.else_statements) { destroy_pval(item->u3.else_statements); } break; case PV_SWITCH: /* fields: item->u1.str == the switch expression item->u2.statements == a pval list of statements in the switch, (will be case statements, most likely!) */ if (item->u1.str) free(item->u1.str); destroy_pval(item->u2.statements); break; case PV_EXTENSION: /* fields: item->u1.str == the extension name, label, whatever it's called item->u2.statements == a pval list of statements in the extension item->u3.hints == a char * hint argument item->u4.regexten == an int boolean. non-zero says that regexten was specified */ if (item->u1.str) free(item->u1.str); if (item->u3.hints) free(item->u3.hints); destroy_pval(item->u2.statements); break; case PV_IGNOREPAT: /* fields: item->u1.str == the ignorepat data */ if (item->u1.str) free(item->u1.str); break; case PV_GLOBALS: /* fields: item->u1.statements == pval list of statements, usually vardecs */ destroy_pval(item->u1.statements); break; } free(item); } void destroy_pval(pval *item) { pval *i,*nxt; for (i=item; i; i=nxt) { nxt = i->next; destroy_pval_item(i); } } #ifdef AAL_ARGCHECK static char *ael_funclist[] = { "AGENT", "ARRAY", "BASE64_DECODE", "BASE64_ENCODE", "CALLERID", "CDR", "CHANNEL", "CHECKSIPDOMAIN", "CHECK_MD5", "CURL", "CUT", "DB", "DB_EXISTS", "DUNDILOOKUP", "ENUMLOOKUP", "ENV", "EVAL", "EXISTS", "FIELDQTY", "FILTER", "GROUP", "GROUP_COUNT", "GROUP_LIST", "GROUP_MATCH_COUNT", "IAXPEER", "IF", "IFTIME", "ISNULL", "KEYPADHASH", "LANGUAGE", "LEN", "MATH", "MD5", "MUSICCLASS", "QUEUEAGENTCOUNT", "QUEUE_MEMBER_COUNT", "QUEUE_MEMBER_LIST", "QUOTE", "RAND", "REGEX", "SET", "SHA1", "SIPCHANINFO", "SIPPEER", "SIP_HEADER", "SORT", "STAT", "STRFTIME", "STRPTIME", "TIMEOUT", "TXTCIDNAME", "URIDECODE", "URIENCODE", "VMCOUNT" }; int ael_is_funcname(char *name) { int s,t; t = sizeof(ael_funclist)/sizeof(char*); s = 0; while ((s < t) && strcasecmp(name, ael_funclist[s])) s++; if ( s < t ) return 1; else return 0; } #endif asterisk-1.4.21.2/pbx/pbx_config.c0000644000175000017500000020756411021541103016103 0ustar maniacmaniac/* * Asterisk -- An open source telephony toolkit. * * Copyright (C) 1999 - 2006, Digium, Inc. * * Mark Spencer * * 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 Populate and remember extensions from static config file * * */ #include "asterisk.h" ASTERISK_FILE_VERSION(__FILE__, "$Revision: 120371 $") #include #include #include #include #include #include #include "asterisk/pbx.h" #include "asterisk/config.h" #include "asterisk/options.h" #include "asterisk/module.h" #include "asterisk/logger.h" #include "asterisk/cli.h" #include "asterisk/callerid.h" static char *config = "extensions.conf"; static char *registrar = "pbx_config"; static char userscontext[AST_MAX_EXTENSION] = "default"; static int static_config = 0; static int write_protect_config = 1; static int autofallthrough_config = 1; static int clearglobalvars_config = 0; AST_MUTEX_DEFINE_STATIC(save_dialplan_lock); static struct ast_context *local_contexts = NULL; /* * Help for commands provided by this module ... */ static char context_add_extension_help[] = "Usage: dialplan add extension ,,,\n" " into [replace]\n\n" " This command will add new extension into . If there is an\n" " existence of extension with the same priority and last 'replace'\n" " arguments is given here we simply replace this extension.\n" "\n" "Example: dialplan add extension 6123,1,Dial,IAX/216.207.245.56/6123 into local\n" " Now, you can dial 6123 and talk to Markster :)\n"; static char context_remove_extension_help[] = "Usage: dialplan remove extension exten@context [priority]\n" " Remove an extension from a given context. If a priority\n" " is given, only that specific priority from the given extension\n" " will be removed.\n"; static char context_add_ignorepat_help[] = "Usage: dialplan add ignorepat into \n" " This command adds a new ignore pattern into context \n" "\n" "Example: dialplan add ignorepat _3XX into local\n"; static char context_remove_ignorepat_help[] = "Usage: dialplan remove ignorepat from \n" " This command removes an ignore pattern from context \n" "\n" "Example: dialplan remove ignorepat _3XX from local\n"; static char context_add_include_help[] = "Usage: dialplan add include into \n" " Include a context in another context.\n"; static char context_remove_include_help[] = "Usage: dialplan remove include from \n" " Remove an included context from another context.\n"; static char save_dialplan_help[] = "Usage: dialplan save [/path/to/extension/file]\n" " Save dialplan created by pbx_config module.\n" "\n" "Example: dialplan save (/etc/asterisk/extensions.conf)\n" " dialplan save /home/markster (/home/markster/extensions.conf)\n"; static char reload_extensions_help[] = "Usage: dialplan reload\n" " reload extensions.conf without reloading any other modules\n" " This command does not delete global variables unless\n" " clearglobalvars is set to yes in extensions.conf\n"; /* * Implementation of functions provided by this module */ /*! * REMOVE INCLUDE command stuff */ static int handle_context_dont_include_deprecated(int fd, int argc, char *argv[]) { if (argc != 5) return RESULT_SHOWUSAGE; if (strcmp(argv[3], "into")) return RESULT_SHOWUSAGE; if (!ast_context_remove_include(argv[4], argv[2], registrar)) { ast_cli(fd, "We are not including '%s' into '%s' now\n", argv[2], argv[4]); return RESULT_SUCCESS; } ast_cli(fd, "Failed to remove '%s' include from '%s' context\n", argv[2], argv[4]); return RESULT_FAILURE; } static int handle_context_remove_include(int fd, int argc, char *argv[]) { if (argc != 6) { return RESULT_SHOWUSAGE; } if (strcmp(argv[4], "from")) { return RESULT_SHOWUSAGE; } if (!ast_context_remove_include(argv[5], argv[3], registrar)) { ast_cli(fd, "The dialplan no longer includes '%s' into '%s'\n", argv[3], argv[5]); return RESULT_SUCCESS; } ast_cli(fd, "Failed to remove '%s' include from '%s' context\n", argv[3], argv[5]); return RESULT_FAILURE; } /*! \brief return true if 'name' is included by context c */ static int lookup_ci(struct ast_context *c, const char *name) { struct ast_include *i = NULL; if (ast_lock_context(c)) /* error, skip */ return 0; while ( (i = ast_walk_context_includes(c, i)) ) if (!strcmp(name, ast_get_include_name(i))) break; ast_unlock_context(c); return i ? -1 /* success */ : 0; } /*! \brief return true if 'name' is in the ignorepats for context c */ static int lookup_c_ip(struct ast_context *c, const char *name) { struct ast_ignorepat *ip = NULL; if (ast_lock_context(c)) /* error, skip */ return 0; while ( (ip = ast_walk_context_ignorepats(c, ip)) ) if (!strcmp(name, ast_get_ignorepat_name(ip))) break; ast_unlock_context(c); return ip ? -1 /* success */ : 0; } /*! \brief moves to the n-th word in the string, or empty string if none */ static const char *skip_words(const char *p, int n) { int in_blank = 0; for (;n && *p; p++) { if (isblank(*p) /* XXX order is important */ && !in_blank) { n--; /* one word is gone */ in_blank = 1; } else if (/* !is_blank(*p), we know already, && */ in_blank) { in_blank = 0; } } return p; } /*! \brief match the first 'len' chars of word. len==0 always succeeds */ static int partial_match(const char *s, const char *word, int len) { return (len == 0 || !strncmp(s, word, len)); } /*! \brief split extension\@context in two parts, return -1 on error. * The return string is malloc'ed and pointed by *ext */ static int split_ec(const char *src, char **ext, char ** const ctx) { char *c, *e = ast_strdup(src); /* now src is not used anymore */ if (e == NULL) return -1; /* malloc error */ /* now, parse values from 'exten@context' */ *ext = e; c = strchr(e, '@'); if (c == NULL) /* no context part */ *ctx = ""; /* it is not overwritten, anyways */ else { /* found context, check for duplicity ... */ *c++ = '\0'; *ctx = c; if (strchr(c, '@')) { /* two @, not allowed */ free(e); return -1; } } return 0; } /* _X_ is the string we need to complete */ static char *complete_context_dont_include_deprecated(const char *line, const char *word, int pos, int state) { int which = 0; char *res = NULL; int len = strlen(word); /* how many bytes to match */ struct ast_context *c = NULL; if (pos == 2) { /* "dont include _X_" */ if (ast_wrlock_contexts()) { ast_log(LOG_ERROR, "Failed to lock context list\n"); return NULL; } /* walk contexts and their includes, return the n-th match */ while (!res && (c = ast_walk_contexts(c))) { struct ast_include *i = NULL; if (ast_lock_context(c)) /* error ? skip this one */ continue; while ( !res && (i = ast_walk_context_includes(c, i)) ) { const char *i_name = ast_get_include_name(i); struct ast_context *nc = NULL; int already_served = 0; if (!partial_match(i_name, word, len)) continue; /* not matched */ /* check if this include is already served or not */ /* go through all contexts again till we reach actual * context or already_served = 1 */ while ( (nc = ast_walk_contexts(nc)) && nc != c && !already_served) already_served = lookup_ci(nc, i_name); if (!already_served && ++which > state) res = strdup(i_name); } ast_unlock_context(c); } ast_unlock_contexts(); return res; } else if (pos == 3) { /* "dont include CTX _X_" */ /* * complete as 'in', but only if previous context is really * included somewhere */ char *context, *dupline; const char *s = skip_words(line, 2); /* skip 'dont' 'include' */ if (state > 0) return NULL; context = dupline = strdup(s); if (!dupline) { ast_log(LOG_ERROR, "Out of free memory\n"); return NULL; } strsep(&dupline, " "); if (ast_rdlock_contexts()) { ast_log(LOG_ERROR, "Failed to lock contexts list\n"); free(context); return NULL; } /* go through all contexts and check if is included ... */ while (!res && (c = ast_walk_contexts(c))) if (lookup_ci(c, context)) /* context is really included, complete "in" command */ res = strdup("in"); ast_unlock_contexts(); if (!res) ast_log(LOG_WARNING, "%s not included anywhere\n", context); free(context); return res; } else if (pos == 4) { /* "dont include CTX in _X_" */ /* * Context from which we removing include ... */ char *context, *dupline, *in; const char *s = skip_words(line, 2); /* skip 'dont' 'include' */ context = dupline = strdup(s); if (!dupline) { ast_log(LOG_ERROR, "Out of free memory\n"); return NULL; } strsep(&dupline, " "); /* skip context */ /* third word must be 'in' */ in = strsep(&dupline, " "); if (!in || strcmp(in, "in")) { free(context); return NULL; } if (ast_rdlock_contexts()) { ast_log(LOG_ERROR, "Failed to lock context list\n"); free(context); return NULL; } /* walk through all contexts ... */ c = NULL; while ( !res && (c = ast_walk_contexts(c))) { const char *c_name = ast_get_context_name(c); if (!partial_match(c_name, word, len)) /* not a good target */ continue; /* walk through all includes and check if it is our context */ if (lookup_ci(c, context) && ++which > state) res = strdup(c_name); } ast_unlock_contexts(); free(context); return res; } return NULL; } static char *complete_context_remove_include(const char *line, const char *word, int pos, int state) { int which = 0; char *res = NULL; int len = strlen(word); /* how many bytes to match */ struct ast_context *c = NULL; if (pos == 3) { /* "dialplan remove include _X_" */ if (ast_rdlock_contexts()) { ast_log(LOG_ERROR, "Failed to lock context list\n"); return NULL; } /* walk contexts and their includes, return the n-th match */ while (!res && (c = ast_walk_contexts(c))) { struct ast_include *i = NULL; if (ast_lock_context(c)) /* error ? skip this one */ continue; while ( !res && (i = ast_walk_context_includes(c, i)) ) { const char *i_name = ast_get_include_name(i); struct ast_context *nc = NULL; int already_served = 0; if (!partial_match(i_name, word, len)) continue; /* not matched */ /* check if this include is already served or not */ /* go through all contexts again till we reach actual * context or already_served = 1 */ while ( (nc = ast_walk_contexts(nc)) && nc != c && !already_served) already_served = lookup_ci(nc, i_name); if (!already_served && ++which > state) res = strdup(i_name); } ast_unlock_context(c); } ast_unlock_contexts(); return res; } else if (pos == 4) { /* "dialplan remove include CTX _X_" */ /* * complete as 'from', but only if previous context is really * included somewhere */ char *context, *dupline; const char *s = skip_words(line, 3); /* skip 'dialplan' 'remove' 'include' */ if (state > 0) return NULL; context = dupline = strdup(s); if (!dupline) { ast_log(LOG_ERROR, "Out of free memory\n"); return NULL; } strsep(&dupline, " "); if (ast_rdlock_contexts()) { ast_log(LOG_ERROR, "Failed to lock contexts list\n"); free(context); return NULL; } /* go through all contexts and check if is included ... */ while (!res && (c = ast_walk_contexts(c))) if (lookup_ci(c, context)) /* context is really included, complete "from" command */ res = strdup("from"); ast_unlock_contexts(); if (!res) ast_log(LOG_WARNING, "%s not included anywhere\n", context); free(context); return res; } else if (pos == 5) { /* "dialplan remove include CTX from _X_" */ /* * Context from which we removing include ... */ char *context, *dupline, *from; const char *s = skip_words(line, 3); /* skip 'dialplan' 'remove' 'include' */ context = dupline = strdup(s); if (!dupline) { ast_log(LOG_ERROR, "Out of free memory\n"); return NULL; } strsep(&dupline, " "); /* skip context */ /* fourth word must be 'from' */ from = strsep(&dupline, " "); if (!from || strcmp(from, "from")) { free(context); return NULL; } if (ast_rdlock_contexts()) { ast_log(LOG_ERROR, "Failed to lock context list\n"); free(context); return NULL; } /* walk through all contexts ... */ c = NULL; while ( !res && (c = ast_walk_contexts(c))) { const char *c_name = ast_get_context_name(c); if (!partial_match(c_name, word, len)) /* not a good target */ continue; /* walk through all includes and check if it is our context */ if (lookup_ci(c, context) && ++which > state) res = strdup(c_name); } ast_unlock_contexts(); free(context); return res; } return NULL; } /*! * REMOVE EXTENSION command stuff */ static int handle_context_remove_extension_deprecated(int fd, int argc, char *argv[]) { int removing_priority = 0; char *exten, *context; int ret = RESULT_FAILURE; if (argc != 4 && argc != 3) return RESULT_SHOWUSAGE; /* * Priority input checking ... */ if (argc == 4) { char *c = argv[3]; /* check for digits in whole parameter for right priority ... * why? because atoi (strtol) returns 0 if any characters in * string and whole extension will be removed, it's not good */ if (!strcmp("hint", c)) removing_priority = PRIORITY_HINT; else { while (*c && isdigit(*c)) c++; if (*c) { /* non-digit in string */ ast_cli(fd, "Invalid priority '%s'\n", argv[3]); return RESULT_FAILURE; } removing_priority = atoi(argv[3]); } if (removing_priority == 0) { ast_cli(fd, "If you want to remove whole extension, please " \ "omit priority argument\n"); return RESULT_FAILURE; } } /* XXX original overwrote argv[2] */ /* * Format exten@context checking ... */ if (split_ec(argv[2], &exten, &context)) return RESULT_FAILURE; /* XXX malloc failure */ if ((!strlen(exten)) || (!(strlen(context)))) { ast_cli(fd, "Missing extension or context name in second argument '%s'\n", argv[2]); free(exten); return RESULT_FAILURE; } if (!ast_context_remove_extension(context, exten, removing_priority, registrar)) { if (!removing_priority) ast_cli(fd, "Whole extension %s@%s removed\n", exten, context); else ast_cli(fd, "Extension %s@%s with priority %d removed\n", exten, context, removing_priority); ret = RESULT_SUCCESS; } else { ast_cli(fd, "Failed to remove extension %s@%s\n", exten, context); ret = RESULT_FAILURE; } free(exten); return ret; } static int handle_context_remove_extension(int fd, int argc, char *argv[]) { int removing_priority = 0; char *exten, *context; int ret = RESULT_FAILURE; if (argc != 5 && argc != 4) return RESULT_SHOWUSAGE; /* * Priority input checking ... */ if (argc == 5) { char *c = argv[4]; /* check for digits in whole parameter for right priority ... * why? because atoi (strtol) returns 0 if any characters in * string and whole extension will be removed, it's not good */ if (!strcmp("hint", c)) removing_priority = PRIORITY_HINT; else { while (*c && isdigit(*c)) c++; if (*c) { /* non-digit in string */ ast_cli(fd, "Invalid priority '%s'\n", argv[4]); return RESULT_FAILURE; } removing_priority = atoi(argv[4]); } if (removing_priority == 0) { ast_cli(fd, "If you want to remove whole extension, please " \ "omit priority argument\n"); return RESULT_FAILURE; } } /* XXX original overwrote argv[3] */ /* * Format exten@context checking ... */ if (split_ec(argv[3], &exten, &context)) return RESULT_FAILURE; /* XXX malloc failure */ if ((!strlen(exten)) || (!(strlen(context)))) { ast_cli(fd, "Missing extension or context name in third argument '%s'\n", argv[3]); free(exten); return RESULT_FAILURE; } if (!ast_context_remove_extension(context, exten, removing_priority, registrar)) { if (!removing_priority) ast_cli(fd, "Whole extension %s@%s removed\n", exten, context); else ast_cli(fd, "Extension %s@%s with priority %d removed\n", exten, context, removing_priority); ret = RESULT_SUCCESS; } else { ast_cli(fd, "Failed to remove extension %s@%s\n", exten, context); ret = RESULT_FAILURE; } free(exten); return ret; } #define BROKEN_READLINE 1 #ifdef BROKEN_READLINE /* * There is one funny thing, when you have word like 300@ and you hit * , you arguments will like as your word is '300 ', so it '@' * characters acts sometimes as word delimiter and sometimes as a part * of word * * This fix function, allocates new word variable and store here every * time xxx@yyy always as one word and correct pos is set too * * It's ugly, I know, but I'm waiting for Mark suggestion if upper is * bug or feature ... */ static int fix_complete_args(const char *line, char **word, int *pos) { char *_line, *_strsep_line, *_previous_word = NULL, *_word = NULL; int words = 0; _line = strdup(line); _strsep_line = _line; while (_strsep_line) { _previous_word = _word; _word = strsep(&_strsep_line, " "); if (_word && strlen(_word)) words++; } if (_word || _previous_word) { if (_word) { if (!strlen(_word)) words++; *word = strdup(_word); } else *word = strdup(_previous_word); *pos = words - 1; free(_line); return 0; } free(_line); return -1; } #endif /* BROKEN_READLINE */ static char *complete_context_remove_extension_deprecated(const char *line, const char *word, int pos, int state) { char *ret = NULL; int which = 0; #ifdef BROKEN_READLINE char *word2; /* * Fix arguments, *word is a new allocated structure, REMEMBER to * free *word when you want to return from this function ... */ if (fix_complete_args(line, &word2, &pos)) { ast_log(LOG_ERROR, "Out of free memory\n"); return NULL; } word = word2; #endif if (pos == 2) { /* 'remove extension _X_' (exten@context ... */ struct ast_context *c = NULL; char *context = NULL, *exten = NULL; int le = 0; /* length of extension */ int lc = 0; /* length of context */ lc = split_ec(word, &exten, &context); #ifdef BROKEN_READLINE free(word2); #endif if (lc) /* error */ return NULL; le = strlen(exten); lc = strlen(context); if (ast_rdlock_contexts()) { ast_log(LOG_ERROR, "Failed to lock context list\n"); goto error2; } /* find our context ... */ while ( (c = ast_walk_contexts(c)) ) { /* match our context if any */ struct ast_exten *e = NULL; /* XXX locking ? */ if (!partial_match(ast_get_context_name(c), context, lc)) continue; /* context not matched */ while ( (e = ast_walk_context_extensions(c, e)) ) { /* try to complete extensions ... */ if ( partial_match(ast_get_extension_name(e), exten, le) && ++which > state) { /* n-th match */ /* If there is an extension then return exten@context. XXX otherwise ? */ if (exten) asprintf(&ret, "%s@%s", ast_get_extension_name(e), ast_get_context_name(c)); break; } } if (e) /* got a match */ break; } ast_unlock_contexts(); error2: if (exten) free(exten); } else if (pos == 3) { /* 'remove extension EXT _X_' (priority) */ char *exten = NULL, *context, *p; struct ast_context *c; int le, lc, len; const char *s = skip_words(line, 2); /* skip 'remove' 'extension' */ int i = split_ec(s, &exten, &context); /* parse ext@context */ if (i) /* error */ goto error3; if ( (p = strchr(exten, ' ')) ) /* remove space after extension */ *p = '\0'; if ( (p = strchr(context, ' ')) ) /* remove space after context */ *p = '\0'; le = strlen(exten); lc = strlen(context); len = strlen(word); if (le == 0 || lc == 0) goto error3; if (ast_rdlock_contexts()) { ast_log(LOG_ERROR, "Failed to lock context list\n"); goto error3; } /* walk contexts */ c = NULL; while ( (c = ast_walk_contexts(c)) ) { /* XXX locking on c ? */ struct ast_exten *e; if (strcmp(ast_get_context_name(c), context) != 0) continue; /* got it, we must match here */ e = NULL; while ( (e = ast_walk_context_extensions(c, e)) ) { struct ast_exten *priority; char buffer[10]; if (strcmp(ast_get_extension_name(e), exten) != 0) continue; /* XXX lock e ? */ priority = NULL; while ( !ret && (priority = ast_walk_extension_priorities(e, priority)) ) { snprintf(buffer, sizeof(buffer), "%u", ast_get_extension_priority(priority)); if (partial_match(buffer, word, len) && ++which > state) /* n-th match */ ret = strdup(buffer); } break; } break; } ast_unlock_contexts(); error3: if (exten) free(exten); #ifdef BROKEN_READLINE free(word2); #endif } return ret; } static char *complete_context_remove_extension(const char *line, const char *word, int pos, int state) { char *ret = NULL; int which = 0; #ifdef BROKEN_READLINE char *word2; /* * Fix arguments, *word is a new allocated structure, REMEMBER to * free *word when you want to return from this function ... */ if (fix_complete_args(line, &word2, &pos)) { ast_log(LOG_ERROR, "Out of free memory\n"); return NULL; } word = word2; #endif if (pos == 3) { /* 'dialplan remove extension _X_' (exten@context ... */ struct ast_context *c = NULL; char *context = NULL, *exten = NULL; int le = 0; /* length of extension */ int lc = 0; /* length of context */ lc = split_ec(word, &exten, &context); #ifdef BROKEN_READLINE free(word2); #endif if (lc) /* error */ return NULL; le = strlen(exten); lc = strlen(context); if (ast_rdlock_contexts()) { ast_log(LOG_ERROR, "Failed to lock context list\n"); goto error2; } /* find our context ... */ while ( (c = ast_walk_contexts(c)) ) { /* match our context if any */ struct ast_exten *e = NULL; /* XXX locking ? */ if (!partial_match(ast_get_context_name(c), context, lc)) continue; /* context not matched */ while ( (e = ast_walk_context_extensions(c, e)) ) { /* try to complete extensions ... */ if ( partial_match(ast_get_extension_name(e), exten, le) && ++which > state) { /* n-th match */ /* If there is an extension then return exten@context. XXX otherwise ? */ if (exten) asprintf(&ret, "%s@%s", ast_get_extension_name(e), ast_get_context_name(c)); break; } } if (e) /* got a match */ break; } ast_unlock_contexts(); error2: if (exten) free(exten); } else if (pos == 4) { /* 'dialplan remove extension EXT _X_' (priority) */ char *exten = NULL, *context, *p; struct ast_context *c; int le, lc, len; const char *s = skip_words(line, 3); /* skip 'dialplan' 'remove' 'extension' */ int i = split_ec(s, &exten, &context); /* parse ext@context */ if (i) /* error */ goto error3; if ( (p = strchr(exten, ' ')) ) /* remove space after extension */ *p = '\0'; if ( (p = strchr(context, ' ')) ) /* remove space after context */ *p = '\0'; le = strlen(exten); lc = strlen(context); len = strlen(word); if (le == 0 || lc == 0) goto error3; if (ast_rdlock_contexts()) { ast_log(LOG_ERROR, "Failed to lock context list\n"); goto error3; } /* walk contexts */ c = NULL; while ( (c = ast_walk_contexts(c)) ) { /* XXX locking on c ? */ struct ast_exten *e; if (strcmp(ast_get_context_name(c), context) != 0) continue; /* got it, we must match here */ e = NULL; while ( (e = ast_walk_context_extensions(c, e)) ) { struct ast_exten *priority; char buffer[10]; if (strcmp(ast_get_extension_name(e), exten) != 0) continue; /* XXX lock e ? */ priority = NULL; while ( !ret && (priority = ast_walk_extension_priorities(e, priority)) ) { snprintf(buffer, sizeof(buffer), "%u", ast_get_extension_priority(priority)); if (partial_match(buffer, word, len) && ++which > state) /* n-th match */ ret = strdup(buffer); } break; } break; } ast_unlock_contexts(); error3: if (exten) free(exten); #ifdef BROKEN_READLINE free(word2); #endif } return ret; } /*! * Include context ... */ static int handle_context_add_include_deprecated(int fd, int argc, char *argv[]) { if (argc != 5) /* include context CTX in CTX */ return RESULT_SHOWUSAGE; /* third arg must be 'in' ... */ if (strcmp(argv[3], "in") && strcmp(argv[3], "into")) /* XXX why both ? */ return RESULT_SHOWUSAGE; if (ast_context_add_include(argv[4], argv[2], registrar)) { switch (errno) { case ENOMEM: ast_cli(fd, "Out of memory for context addition\n"); break; case EBUSY: ast_cli(fd, "Failed to lock context(s) list, please try again later\n"); break; case EEXIST: ast_cli(fd, "Context '%s' already included in '%s' context\n", argv[2], argv[4]); break; case ENOENT: case EINVAL: ast_cli(fd, "There is no existence of context '%s'\n", errno == ENOENT ? argv[4] : argv[2]); break; default: ast_cli(fd, "Failed to include '%s' in '%s' context\n", argv[2], argv[4]); break; } return RESULT_FAILURE; } /* show some info ... */ ast_cli(fd, "Context '%s' included in '%s' context\n", argv[2], argv[4]); return RESULT_SUCCESS; } static int handle_context_add_include(int fd, int argc, char *argv[]) { if (argc != 6) /* dialplan add include CTX in CTX */ return RESULT_SHOWUSAGE; /* fifth arg must be 'into' ... */ if (strcmp(argv[4], "into")) return RESULT_SHOWUSAGE; if (ast_context_add_include(argv[5], argv[3], registrar)) { switch (errno) { case ENOMEM: ast_cli(fd, "Out of memory for context addition\n"); break; case EBUSY: ast_cli(fd, "Failed to lock context(s) list, please try again later\n"); break; case EEXIST: ast_cli(fd, "Context '%s' already included in '%s' context\n", argv[3], argv[5]); break; case ENOENT: case EINVAL: ast_cli(fd, "There is no existence of context '%s'\n", errno == ENOENT ? argv[5] : argv[3]); break; default: ast_cli(fd, "Failed to include '%s' in '%s' context\n", argv[3], argv[5]); break; } return RESULT_FAILURE; } /* show some info ... */ ast_cli(fd, "Context '%s' included in '%s' context\n", argv[3], argv[5]); return RESULT_SUCCESS; } static char *complete_context_add_include_deprecated(const char *line, const char *word, int pos, int state) { struct ast_context *c; int which = 0; char *ret = NULL; int len = strlen(word); if (pos == 2) { /* 'include context _X_' (context) ... */ if (ast_rdlock_contexts()) { ast_log(LOG_ERROR, "Failed to lock context list\n"); return NULL; } for (c = NULL; !ret && (c = ast_walk_contexts(c)); ) if (partial_match(ast_get_context_name(c), word, len) && ++which > state) ret = strdup(ast_get_context_name(c)); ast_unlock_contexts(); return ret; } else if (pos == 3) { /* include context CTX _X_ */ /* complete as 'in' if context exists or we are unable to check */ char *context, *dupline; struct ast_context *c; const char *s = skip_words(line, 2); /* should not fail */ if (state != 0) /* only once */ return NULL; /* parse context from line ... */ context = dupline = strdup(s); if (!context) { ast_log(LOG_ERROR, "Out of free memory\n"); return strdup("in"); } strsep(&dupline, " "); /* check for context existence ... */ if (ast_rdlock_contexts()) { ast_log(LOG_ERROR, "Failed to lock context list\n"); /* our fault, we can't check, so complete 'in' ... */ ret = strdup("in"); } else { for (c = NULL; !ret && (c = ast_walk_contexts(c)); ) if (!strcmp(context, ast_get_context_name(c))) ret = strdup("in"); /* found */ ast_unlock_contexts(); } free(context); return ret; } else if (pos == 4) { /* 'include context CTX in _X_' (dst context) */ char *context, *dupline, *in; const char *s = skip_words(line, 2); /* should not fail */ context = dupline = strdup(s); if (!dupline) { ast_log(LOG_ERROR, "Out of free memory\n"); return NULL; } strsep(&dupline, " "); /* skip context */ in = strsep(&dupline, " "); /* error if missing context or third word is not 'in' */ if (!strlen(context) || strcmp(in, "in")) { ast_log(LOG_ERROR, "bad context %s or missing in %s\n", context, in); goto error3; } if (ast_rdlock_contexts()) { ast_log(LOG_ERROR, "Failed to lock context list\n"); goto error3; } for (c = NULL; (c = ast_walk_contexts(c)); ) if (!strcmp(context, ast_get_context_name(c))) break; if (c) { /* first context exists, go on... */ /* go through all contexts ... */ for (c = NULL; !ret && (c = ast_walk_contexts(c)); ) { if (!strcmp(context, ast_get_context_name(c))) continue; /* skip ourselves */ if (partial_match(ast_get_context_name(c), word, len) && !lookup_ci(c, context) /* not included yet */ && ++which > state) ret = strdup(ast_get_context_name(c)); } } else { ast_log(LOG_ERROR, "context %s not found\n", context); } ast_unlock_contexts(); error3: free(context); return ret; } return NULL; } static char *complete_context_add_include(const char *line, const char *word, int pos, int state) { struct ast_context *c; int which = 0; char *ret = NULL; int len = strlen(word); if (pos == 3) { /* 'dialplan add include _X_' (context) ... */ if (ast_rdlock_contexts()) { ast_log(LOG_ERROR, "Failed to lock context list\n"); return NULL; } for (c = NULL; !ret && (c = ast_walk_contexts(c)); ) if (partial_match(ast_get_context_name(c), word, len) && ++which > state) ret = strdup(ast_get_context_name(c)); ast_unlock_contexts(); return ret; } else if (pos == 4) { /* dialplan add include CTX _X_ */ /* complete as 'into' if context exists or we are unable to check */ char *context, *dupline; struct ast_context *c; const char *s = skip_words(line, 3); /* should not fail */ if (state != 0) /* only once */ return NULL; /* parse context from line ... */ context = dupline = strdup(s); if (!context) { ast_log(LOG_ERROR, "Out of free memory\n"); return strdup("into"); } strsep(&dupline, " "); /* check for context existence ... */ if (ast_rdlock_contexts()) { ast_log(LOG_ERROR, "Failed to lock context list\n"); /* our fault, we can't check, so complete 'into' ... */ ret = strdup("into"); } else { for (c = NULL; !ret && (c = ast_walk_contexts(c)); ) if (!strcmp(context, ast_get_context_name(c))) ret = strdup("into"); /* found */ ast_unlock_contexts(); } free(context); return ret; } else if (pos == 5) { /* 'dialplan add include CTX into _X_' (dst context) */ char *context, *dupline, *into; const char *s = skip_words(line, 3); /* should not fail */ context = dupline = strdup(s); if (!dupline) { ast_log(LOG_ERROR, "Out of free memory\n"); return NULL; } strsep(&dupline, " "); /* skip context */ into = strsep(&dupline, " "); /* error if missing context or fifth word is not 'into' */ if (!strlen(context) || strcmp(into, "into")) { ast_log(LOG_ERROR, "bad context %s or missing into %s\n", context, into); goto error3; } if (ast_rdlock_contexts()) { ast_log(LOG_ERROR, "Failed to lock context list\n"); goto error3; } for (c = NULL; (c = ast_walk_contexts(c)); ) if (!strcmp(context, ast_get_context_name(c))) break; if (c) { /* first context exists, go on... */ /* go through all contexts ... */ for (c = NULL; !ret && (c = ast_walk_contexts(c)); ) { if (!strcmp(context, ast_get_context_name(c))) continue; /* skip ourselves */ if (partial_match(ast_get_context_name(c), word, len) && !lookup_ci(c, context) /* not included yet */ && ++which > state) ret = strdup(ast_get_context_name(c)); } } else { ast_log(LOG_ERROR, "context %s not found\n", context); } ast_unlock_contexts(); error3: free(context); return ret; } return NULL; } /*! * \brief 'save dialplan' CLI command implementation functions ... */ static int handle_save_dialplan(int fd, int argc, char *argv[]) { char filename[256]; struct ast_context *c; struct ast_config *cfg; struct ast_variable *v; int incomplete = 0; /* incomplete config write? */ FILE *output; const char *base, *slash, *file; if (! (static_config && !write_protect_config)) { ast_cli(fd, "I can't save dialplan now, see '%s' example file.\n", config); return RESULT_FAILURE; } if (argc != 2 && argc != 3) return RESULT_SHOWUSAGE; if (ast_mutex_lock(&save_dialplan_lock)) { ast_cli(fd, "Failed to lock dialplan saving (another proccess saving?)\n"); return RESULT_FAILURE; } /* XXX the code here is quite loose, a pathname with .conf in it * is assumed to be a complete pathname */ if (argc == 3) { /* have config path. Look for *.conf */ base = argv[2]; if (!strstr(argv[2], ".conf")) { /*no, this is assumed to be a pathname */ /* if filename ends with '/', do not add one */ slash = (*(argv[2] + strlen(argv[2]) -1) == '/') ? "/" : ""; file = config; /* default: 'extensions.conf' */ } else { /* yes, complete file name */ slash = ""; file = ""; } } else { /* no config file, default one */ base = ast_config_AST_CONFIG_DIR; slash = "/"; file = config; } snprintf(filename, sizeof(filename), "%s%s%s", base, slash, config); cfg = ast_config_load("extensions.conf"); /* try to lock contexts list */ if (ast_rdlock_contexts()) { ast_cli(fd, "Failed to lock contexts list\n"); ast_mutex_unlock(&save_dialplan_lock); ast_config_destroy(cfg); return RESULT_FAILURE; } /* create new file ... */ if (!(output = fopen(filename, "wt"))) { ast_cli(fd, "Failed to create file '%s'\n", filename); ast_unlock_contexts(); ast_mutex_unlock(&save_dialplan_lock); ast_config_destroy(cfg); return RESULT_FAILURE; } /* fireout general info */ fprintf(output, "[general]\nstatic=%s\nwriteprotect=%s\nautofallthrough=%s\nclearglobalvars=%s\npriorityjumping=%s\n\n", static_config ? "yes" : "no", write_protect_config ? "yes" : "no", autofallthrough_config ? "yes" : "no", clearglobalvars_config ? "yes" : "no", ast_true(ast_variable_retrieve(cfg, "general", "priorityjumping")) ? "yes" : "no"); if ((v = ast_variable_browse(cfg, "globals"))) { fprintf(output, "[globals]\n"); while(v) { fprintf(output, "%s => %s\n", v->name, v->value); v = v->next; } fprintf(output, "\n"); } ast_config_destroy(cfg); #define PUT_CTX_HDR do { \ if (!context_header_written) { \ fprintf(output, "[%s]\n", ast_get_context_name(c)); \ context_header_written = 1; \ } \ } while (0) /* walk all contexts */ for (c = NULL; (c = ast_walk_contexts(c)); ) { int context_header_written = 0; struct ast_exten *e, *last_written_e = NULL; struct ast_include *i; struct ast_ignorepat *ip; struct ast_sw *sw; /* try to lock context and fireout all info */ if (ast_lock_context(c)) { /* lock failure */ incomplete = 1; continue; } /* registered by this module? */ /* XXX do we need this ? */ if (!strcmp(ast_get_context_registrar(c), registrar)) { fprintf(output, "[%s]\n", ast_get_context_name(c)); context_header_written = 1; } /* walk extensions ... */ for (e = NULL; (e = ast_walk_context_extensions(c, e)); ) { struct ast_exten *p = NULL; /* fireout priorities */ while ( (p = ast_walk_extension_priorities(e, p)) ) { if (strcmp(ast_get_extension_registrar(p), registrar) != 0) /* not this source */ continue; /* make empty line between different extensions */ if (last_written_e != NULL && strcmp(ast_get_extension_name(last_written_e), ast_get_extension_name(p))) fprintf(output, "\n"); last_written_e = p; PUT_CTX_HDR; if (ast_get_extension_priority(p)==PRIORITY_HINT) { /* easy */ fprintf(output, "exten => %s,hint,%s\n", ast_get_extension_name(p), ast_get_extension_app(p)); } else { /* copy and replace '|' with ',' */ const char *sep, *cid; char *tempdata = ""; char *s; const char *el = ast_get_extension_label(p); char label[128] = ""; s = ast_get_extension_app_data(p); if (s) { char *t; tempdata = alloca(strlen(tempdata) * 2 + 1); for (t = tempdata; *s; s++, t++) { if (*s == '|') *t = ','; else { if (*s == ',' || *s == ';') *t++ = '\\'; *t = *s; } } /* Terminating NULL */ *t = *s; } if (ast_get_extension_matchcid(p)) { sep = "/"; cid = ast_get_extension_cidmatch(p); } else sep = cid = ""; if (el && (snprintf(label, sizeof(label), "(%s)", el) != (strlen(el) + 2))) incomplete = 1; /* error encountered or label > 125 chars */ fprintf(output, "exten => %s%s%s,%d%s,%s(%s)\n", ast_get_extension_name(p), (ast_strlen_zero(sep) ? "" : sep), (ast_strlen_zero(cid) ? "" : cid), ast_get_extension_priority(p), label, ast_get_extension_app(p), (ast_strlen_zero(tempdata) ? "" : tempdata)); } } } /* written any extensions? ok, write space between exten & inc */ if (last_written_e) fprintf(output, "\n"); /* walk through includes */ for (i = NULL; (i = ast_walk_context_includes(c, i)) ; ) { if (strcmp(ast_get_include_registrar(i), registrar) != 0) continue; /* not mine */ PUT_CTX_HDR; fprintf(output, "include => %s\n", ast_get_include_name(i)); } if (ast_walk_context_includes(c, NULL)) fprintf(output, "\n"); /* walk through switches */ for (sw = NULL; (sw = ast_walk_context_switches(c, sw)) ; ) { if (strcmp(ast_get_switch_registrar(sw), registrar) != 0) continue; /* not mine */ PUT_CTX_HDR; fprintf(output, "switch => %s/%s\n", ast_get_switch_name(sw), ast_get_switch_data(sw)); } if (ast_walk_context_switches(c, NULL)) fprintf(output, "\n"); /* fireout ignorepats ... */ for (ip = NULL; (ip = ast_walk_context_ignorepats(c, ip)); ) { if (strcmp(ast_get_ignorepat_registrar(ip), registrar) != 0) continue; /* not mine */ PUT_CTX_HDR; fprintf(output, "ignorepat => %s\n", ast_get_ignorepat_name(ip)); } ast_unlock_context(c); } ast_unlock_contexts(); ast_mutex_unlock(&save_dialplan_lock); fclose(output); if (incomplete) { ast_cli(fd, "Saved dialplan is incomplete\n"); return RESULT_FAILURE; } ast_cli(fd, "Dialplan successfully saved into '%s'\n", filename); return RESULT_SUCCESS; } /*! * \brief ADD EXTENSION command stuff */ static int handle_context_add_extension_deprecated(int fd, int argc, char *argv[]) { char *whole_exten; char *exten, *prior; int iprior = -2; char *cidmatch, *app, *app_data; char *start, *end; /* check for arguments at first */ if (argc != 5 && argc != 6) return RESULT_SHOWUSAGE; if (strcmp(argv[3], "into")) return RESULT_SHOWUSAGE; if (argc == 6) if (strcmp(argv[5], "replace")) return RESULT_SHOWUSAGE; /* XXX overwrite argv[2] */ whole_exten = argv[2]; exten = strsep(&whole_exten,","); if (strchr(exten, '/')) { cidmatch = exten; strsep(&cidmatch,"/"); } else { cidmatch = NULL; } prior = strsep(&whole_exten,","); if (prior) { if (!strcmp(prior, "hint")) { iprior = PRIORITY_HINT; } else { if (sscanf(prior, "%d", &iprior) != 1) { ast_cli(fd, "'%s' is not a valid priority\n", prior); prior = NULL; } } } app = whole_exten; if (app && (start = strchr(app, '(')) && (end = strrchr(app, ')'))) { *start = *end = '\0'; app_data = start + 1; ast_process_quotes_and_slashes(app_data, ',', '|'); } else { if (app) { app_data = strchr(app, ','); if (app_data) { *app_data = '\0'; app_data++; } } else app_data = NULL; } if (!exten || !prior || !app || (!app_data && iprior != PRIORITY_HINT)) return RESULT_SHOWUSAGE; if (!app_data) app_data=""; if (ast_add_extension(argv[4], argc == 6 ? 1 : 0, exten, iprior, NULL, cidmatch, app, (void *)strdup(app_data), ast_free, registrar)) { switch (errno) { case ENOMEM: ast_cli(fd, "Out of free memory\n"); break; case EBUSY: ast_cli(fd, "Failed to lock context(s) list, please try again later\n"); break; case ENOENT: ast_cli(fd, "No existence of '%s' context\n", argv[4]); break; case EEXIST: ast_cli(fd, "Extension %s@%s with priority %s already exists\n", exten, argv[4], prior); break; default: ast_cli(fd, "Failed to add '%s,%s,%s,%s' extension into '%s' context\n", exten, prior, app, app_data, argv[4]); break; } return RESULT_FAILURE; } if (argc == 6) ast_cli(fd, "Extension %s@%s (%s) replace by '%s,%s,%s,%s'\n", exten, argv[4], prior, exten, prior, app, app_data); else ast_cli(fd, "Extension '%s,%s,%s,%s' added into '%s' context\n", exten, prior, app, app_data, argv[4]); return RESULT_SUCCESS; } static int handle_context_add_extension(int fd, int argc, char *argv[]) { char *whole_exten; char *exten, *prior; int iprior = -2; char *cidmatch, *app, *app_data; char *start, *end; /* check for arguments at first */ if (argc != 6 && argc != 7) return RESULT_SHOWUSAGE; if (strcmp(argv[4], "into")) return RESULT_SHOWUSAGE; if (argc == 7) if (strcmp(argv[6], "replace")) return RESULT_SHOWUSAGE; /* XXX overwrite argv[3] */ whole_exten = argv[3]; exten = strsep(&whole_exten,","); if (strchr(exten, '/')) { cidmatch = exten; strsep(&cidmatch,"/"); } else { cidmatch = NULL; } prior = strsep(&whole_exten,","); if (prior) { if (!strcmp(prior, "hint")) { iprior = PRIORITY_HINT; } else { if (sscanf(prior, "%d", &iprior) != 1) { ast_cli(fd, "'%s' is not a valid priority\n", prior); prior = NULL; } } } app = whole_exten; if (app && (start = strchr(app, '(')) && (end = strrchr(app, ')'))) { *start = *end = '\0'; app_data = start + 1; ast_process_quotes_and_slashes(app_data, ',', '|'); } else { if (app) { app_data = strchr(app, ','); if (app_data) { *app_data = '\0'; app_data++; } } else app_data = NULL; } if (!exten || !prior || !app || (!app_data && iprior != PRIORITY_HINT)) return RESULT_SHOWUSAGE; if (!app_data) app_data=""; if (ast_add_extension(argv[5], argc == 7 ? 1 : 0, exten, iprior, NULL, cidmatch, app, (void *)strdup(app_data), ast_free, registrar)) { switch (errno) { case ENOMEM: ast_cli(fd, "Out of free memory\n"); break; case EBUSY: ast_cli(fd, "Failed to lock context(s) list, please try again later\n"); break; case ENOENT: ast_cli(fd, "No existence of '%s' context\n", argv[5]); break; case EEXIST: ast_cli(fd, "Extension %s@%s with priority %s already exists\n", exten, argv[5], prior); break; default: ast_cli(fd, "Failed to add '%s,%s,%s,%s' extension into '%s' context\n", exten, prior, app, app_data, argv[5]); break; } return RESULT_FAILURE; } if (argc == 7) ast_cli(fd, "Extension %s@%s (%s) replace by '%s,%s,%s,%s'\n", exten, argv[5], prior, exten, prior, app, app_data); else ast_cli(fd, "Extension '%s,%s,%s,%s' added into '%s' context\n", exten, prior, app, app_data, argv[5]); return RESULT_SUCCESS; } /*! dialplan add extension 6123,1,Dial,IAX/212.71.138.13/6123 into local */ static char *complete_context_add_extension_deprecated(const char *line, const char *word, int pos, int state) { int which = 0; if (pos == 3) { /* complete 'into' word ... */ return (state == 0) ? strdup("into") : NULL; } else if (pos == 4) { /* complete context */ struct ast_context *c = NULL; int len = strlen(word); char *res = NULL; /* try to lock contexts list ... */ if (ast_rdlock_contexts()) { ast_log(LOG_WARNING, "Failed to lock contexts list\n"); return NULL; } /* walk through all contexts */ while ( !res && (c = ast_walk_contexts(c)) ) if (partial_match(ast_get_context_name(c), word, len) && ++which > state) res = strdup(ast_get_context_name(c)); ast_unlock_contexts(); return res; } else if (pos == 5) { return state == 0 ? strdup("replace") : NULL; } return NULL; } static char *complete_context_add_extension(const char *line, const char *word, int pos, int state) { int which = 0; if (pos == 4) { /* complete 'into' word ... */ return (state == 0) ? strdup("into") : NULL; } else if (pos == 5) { /* complete context */ struct ast_context *c = NULL; int len = strlen(word); char *res = NULL; /* try to lock contexts list ... */ if (ast_rdlock_contexts()) { ast_log(LOG_WARNING, "Failed to lock contexts list\n"); return NULL; } /* walk through all contexts */ while ( !res && (c = ast_walk_contexts(c)) ) if (partial_match(ast_get_context_name(c), word, len) && ++which > state) res = strdup(ast_get_context_name(c)); ast_unlock_contexts(); return res; } else if (pos == 6) { return state == 0 ? strdup("replace") : NULL; } return NULL; } /*! * IGNOREPAT CLI stuff */ static int handle_context_add_ignorepat_deprecated(int fd, int argc, char *argv[]) { if (argc != 5) return RESULT_SHOWUSAGE; if (strcmp(argv[3], "into")) return RESULT_SHOWUSAGE; if (ast_context_add_ignorepat(argv[4], argv[2], registrar)) { switch (errno) { case ENOMEM: ast_cli(fd, "Out of free memory\n"); break; case ENOENT: ast_cli(fd, "There is no existence of '%s' context\n", argv[4]); break; case EEXIST: ast_cli(fd, "Ignore pattern '%s' already included in '%s' context\n", argv[2], argv[4]); break; case EBUSY: ast_cli(fd, "Failed to lock context(s) list, please, try again later\n"); break; default: ast_cli(fd, "Failed to add ingore pattern '%s' into '%s' context\n", argv[2], argv[4]); break; } return RESULT_FAILURE; } ast_cli(fd, "Ignore pattern '%s' added into '%s' context\n", argv[2], argv[4]); return RESULT_SUCCESS; } static int handle_context_add_ignorepat(int fd, int argc, char *argv[]) { if (argc != 6) return RESULT_SHOWUSAGE; if (strcmp(argv[4], "into")) return RESULT_SHOWUSAGE; if (ast_context_add_ignorepat(argv[5], argv[3], registrar)) { switch (errno) { case ENOMEM: ast_cli(fd, "Out of free memory\n"); break; case ENOENT: ast_cli(fd, "There is no existence of '%s' context\n", argv[5]); break; case EEXIST: ast_cli(fd, "Ignore pattern '%s' already included in '%s' context\n", argv[3], argv[5]); break; case EBUSY: ast_cli(fd, "Failed to lock context(s) list, please, try again later\n"); break; default: ast_cli(fd, "Failed to add ingore pattern '%s' into '%s' context\n", argv[3], argv[5]); break; } return RESULT_FAILURE; } ast_cli(fd, "Ignore pattern '%s' added into '%s' context\n", argv[3], argv[5]); return RESULT_SUCCESS; } static char *complete_context_add_ignorepat_deprecated(const char *line, const char *word, int pos, int state) { if (pos == 3) return state == 0 ? strdup("into") : NULL; else if (pos == 4) { struct ast_context *c; int which = 0; char *dupline, *ignorepat = NULL; const char *s; char *ret = NULL; int len = strlen(word); /* XXX skip first two words 'add' 'ignorepat' */ s = skip_words(line, 2); if (s == NULL) return NULL; dupline = strdup(s); if (!dupline) { ast_log(LOG_ERROR, "Malloc failure\n"); return NULL; } ignorepat = strsep(&dupline, " "); if (ast_rdlock_contexts()) { ast_log(LOG_ERROR, "Failed to lock contexts list\n"); return NULL; } for (c = NULL; !ret && (c = ast_walk_contexts(c));) { int found = 0; if (!partial_match(ast_get_context_name(c), word, len)) continue; /* not mine */ if (ignorepat) /* there must be one, right ? */ found = lookup_c_ip(c, ignorepat); if (!found && ++which > state) ret = strdup(ast_get_context_name(c)); } if (ignorepat) free(ignorepat); ast_unlock_contexts(); return ret; } return NULL; } static char *complete_context_add_ignorepat(const char *line, const char *word, int pos, int state) { if (pos == 4) return state == 0 ? strdup("into") : NULL; else if (pos == 5) { struct ast_context *c; int which = 0; char *dupline, *ignorepat = NULL; const char *s; char *ret = NULL; int len = strlen(word); /* XXX skip first three words 'dialplan' 'add' 'ignorepat' */ s = skip_words(line, 3); if (s == NULL) return NULL; dupline = strdup(s); if (!dupline) { ast_log(LOG_ERROR, "Malloc failure\n"); return NULL; } ignorepat = strsep(&dupline, " "); if (ast_rdlock_contexts()) { ast_log(LOG_ERROR, "Failed to lock contexts list\n"); return NULL; } for (c = NULL; !ret && (c = ast_walk_contexts(c));) { int found = 0; if (!partial_match(ast_get_context_name(c), word, len)) continue; /* not mine */ if (ignorepat) /* there must be one, right ? */ found = lookup_c_ip(c, ignorepat); if (!found && ++which > state) ret = strdup(ast_get_context_name(c)); } if (ignorepat) free(ignorepat); ast_unlock_contexts(); return ret; } return NULL; } static int handle_context_remove_ignorepat_deprecated(int fd, int argc, char *argv[]) { if (argc != 5) return RESULT_SHOWUSAGE; if (strcmp(argv[3], "from")) return RESULT_SHOWUSAGE; if (ast_context_remove_ignorepat(argv[4], argv[2], registrar)) { switch (errno) { case EBUSY: ast_cli(fd, "Failed to lock context(s) list, please try again later\n"); break; case ENOENT: ast_cli(fd, "There is no existence of '%s' context\n", argv[4]); break; case EINVAL: ast_cli(fd, "There is no existence of '%s' ignore pattern in '%s' context\n", argv[2], argv[4]); break; default: ast_cli(fd, "Failed to remove ignore pattern '%s' from '%s' context\n", argv[2], argv[4]); break; } return RESULT_FAILURE; } ast_cli(fd, "Ignore pattern '%s' removed from '%s' context\n", argv[2], argv[4]); return RESULT_SUCCESS; } static int handle_context_remove_ignorepat(int fd, int argc, char *argv[]) { if (argc != 6) return RESULT_SHOWUSAGE; if (strcmp(argv[4], "from")) return RESULT_SHOWUSAGE; if (ast_context_remove_ignorepat(argv[5], argv[3], registrar)) { switch (errno) { case EBUSY: ast_cli(fd, "Failed to lock context(s) list, please try again later\n"); break; case ENOENT: ast_cli(fd, "There is no existence of '%s' context\n", argv[5]); break; case EINVAL: ast_cli(fd, "There is no existence of '%s' ignore pattern in '%s' context\n", argv[3], argv[5]); break; default: ast_cli(fd, "Failed to remove ignore pattern '%s' from '%s' context\n", argv[3], argv[5]); break; } return RESULT_FAILURE; } ast_cli(fd, "Ignore pattern '%s' removed from '%s' context\n", argv[3], argv[5]); return RESULT_SUCCESS; } static char *complete_context_remove_ignorepat_deprecated(const char *line, const char *word, int pos, int state) { struct ast_context *c; int which = 0; char *ret = NULL; if (pos == 2) { int len = strlen(word); if (ast_rdlock_contexts()) { ast_log(LOG_WARNING, "Failed to lock contexts list\n"); return NULL; } for (c = NULL; !ret && (c = ast_walk_contexts(c));) { struct ast_ignorepat *ip; if (ast_lock_context(c)) /* error, skip it */ continue; for (ip = NULL; !ret && (ip = ast_walk_context_ignorepats(c, ip));) { if (partial_match(ast_get_ignorepat_name(ip), word, len) && ++which > state) { /* n-th match */ struct ast_context *cw = NULL; int found = 0; while ( (cw = ast_walk_contexts(cw)) && cw != c && !found) { /* XXX do i stop on c, or skip it ? */ found = lookup_c_ip(cw, ast_get_ignorepat_name(ip)); } if (!found) ret = strdup(ast_get_ignorepat_name(ip)); } } ast_unlock_context(c); } ast_unlock_contexts(); return ret; } else if (pos == 3) { return state == 0 ? strdup("from") : NULL; } else if (pos == 4) { /* XXX check this */ char *dupline, *duplinet, *ignorepat; int len = strlen(word); dupline = strdup(line); if (!dupline) { ast_log(LOG_WARNING, "Out of free memory\n"); return NULL; } duplinet = dupline; strsep(&duplinet, " "); strsep(&duplinet, " "); ignorepat = strsep(&duplinet, " "); if (!ignorepat) { free(dupline); return NULL; } if (ast_rdlock_contexts()) { ast_log(LOG_WARNING, "Failed to lock contexts list\n"); free(dupline); return NULL; } for (c = NULL; !ret && (c = ast_walk_contexts(c)); ) { if (ast_lock_context(c)) /* fail, skip it */ continue; if (!partial_match(ast_get_context_name(c), word, len)) continue; if (lookup_c_ip(c, ignorepat) && ++which > state) ret = strdup(ast_get_context_name(c)); ast_unlock_context(c); } ast_unlock_contexts(); free(dupline); return NULL; } return NULL; } static char *complete_context_remove_ignorepat(const char *line, const char *word, int pos, int state) { struct ast_context *c; int which = 0; char *ret = NULL; if (pos == 3) { int len = strlen(word); if (ast_rdlock_contexts()) { ast_log(LOG_WARNING, "Failed to lock contexts list\n"); return NULL; } for (c = NULL; !ret && (c = ast_walk_contexts(c));) { struct ast_ignorepat *ip; if (ast_lock_context(c)) /* error, skip it */ continue; for (ip = NULL; !ret && (ip = ast_walk_context_ignorepats(c, ip));) { if (partial_match(ast_get_ignorepat_name(ip), word, len) && ++which > state) { /* n-th match */ struct ast_context *cw = NULL; int found = 0; while ( (cw = ast_walk_contexts(cw)) && cw != c && !found) { /* XXX do i stop on c, or skip it ? */ found = lookup_c_ip(cw, ast_get_ignorepat_name(ip)); } if (!found) ret = strdup(ast_get_ignorepat_name(ip)); } } ast_unlock_context(c); } ast_unlock_contexts(); return ret; } else if (pos == 4) { return state == 0 ? strdup("from") : NULL; } else if (pos == 5) { /* XXX check this */ char *dupline, *duplinet, *ignorepat; int len = strlen(word); dupline = strdup(line); if (!dupline) { ast_log(LOG_WARNING, "Out of free memory\n"); return NULL; } duplinet = dupline; strsep(&duplinet, " "); strsep(&duplinet, " "); ignorepat = strsep(&duplinet, " "); if (!ignorepat) { free(dupline); return NULL; } if (ast_rdlock_contexts()) { ast_log(LOG_WARNING, "Failed to lock contexts list\n"); free(dupline); return NULL; } for (c = NULL; !ret && (c = ast_walk_contexts(c)); ) { if (ast_lock_context(c)) /* fail, skip it */ continue; if (!partial_match(ast_get_context_name(c), word, len)) continue; if (lookup_c_ip(c, ignorepat) && ++which > state) ret = strdup(ast_get_context_name(c)); ast_unlock_context(c); } ast_unlock_contexts(); free(dupline); return NULL; } return NULL; } static int pbx_load_module(void); static int handle_reload_extensions(int fd, int argc, char *argv[]) { if (argc != 2) return RESULT_SHOWUSAGE; if (clearglobalvars_config) pbx_builtin_clear_globals(); pbx_load_module(); ast_cli(fd, "Dialplan reloaded.\n"); return RESULT_SUCCESS; } /*! * CLI entries for commands provided by this module */ static struct ast_cli_entry cli_dont_include_deprecated = { { "dont", "include", NULL }, handle_context_dont_include_deprecated, NULL, NULL, complete_context_dont_include_deprecated }; static struct ast_cli_entry cli_remove_extension_deprecated = { { "remove", "extension", NULL }, handle_context_remove_extension_deprecated, NULL, NULL, complete_context_remove_extension_deprecated }; static struct ast_cli_entry cli_include_context_deprecated = { { "include", "context", NULL }, handle_context_add_include_deprecated, NULL, NULL, complete_context_add_include_deprecated }; static struct ast_cli_entry cli_add_extension_deprecated = { { "add", "extension", NULL }, handle_context_add_extension_deprecated, NULL, NULL, complete_context_add_extension_deprecated }; static struct ast_cli_entry cli_add_ignorepat_deprecated = { { "add", "ignorepat", NULL }, handle_context_add_ignorepat_deprecated, NULL, NULL, complete_context_add_ignorepat_deprecated }; static struct ast_cli_entry cli_remove_ignorepat_deprecated = { { "remove", "ignorepat", NULL }, handle_context_remove_ignorepat_deprecated, NULL, NULL, complete_context_remove_ignorepat_deprecated }; static struct ast_cli_entry cli_extensions_reload_deprecated = { { "extensions", "reload", NULL }, handle_reload_extensions, NULL, NULL }; static struct ast_cli_entry cli_save_dialplan_deprecated = { { "save", "dialplan", NULL }, handle_save_dialplan, NULL, NULL }; static struct ast_cli_entry cli_pbx_config[] = { { { "dialplan", "add", "extension", NULL }, handle_context_add_extension, "Add new extension into context", context_add_extension_help, complete_context_add_extension, &cli_add_extension_deprecated }, { { "dialplan", "remove", "extension", NULL }, handle_context_remove_extension, "Remove a specified extension", context_remove_extension_help, complete_context_remove_extension, &cli_remove_extension_deprecated }, { { "dialplan", "add", "ignorepat", NULL }, handle_context_add_ignorepat, "Add new ignore pattern", context_add_ignorepat_help, complete_context_add_ignorepat, &cli_add_ignorepat_deprecated }, { { "dialplan", "remove", "ignorepat", NULL }, handle_context_remove_ignorepat, "Remove ignore pattern from context", context_remove_ignorepat_help, complete_context_remove_ignorepat, &cli_remove_ignorepat_deprecated }, { { "dialplan", "add", "include", NULL }, handle_context_add_include, "Include context in other context", context_add_include_help, complete_context_add_include, &cli_include_context_deprecated }, { { "dialplan", "remove", "include", NULL }, handle_context_remove_include, "Remove a specified include from context", context_remove_include_help, complete_context_remove_include, &cli_dont_include_deprecated }, { { "dialplan", "reload", NULL }, handle_reload_extensions, "Reload extensions and *only* extensions", reload_extensions_help, NULL, &cli_extensions_reload_deprecated }, }; static struct ast_cli_entry cli_dialplan_save = { { "dialplan", "save", NULL }, handle_save_dialplan, "Save dialplan", save_dialplan_help, NULL, &cli_save_dialplan_deprecated }; /*! * Standard module functions ... */ static int unload_module(void) { if (static_config && !write_protect_config) ast_cli_unregister(&cli_dialplan_save); ast_cli_unregister_multiple(cli_pbx_config, sizeof(cli_pbx_config) / sizeof(struct ast_cli_entry)); ast_context_destroy(NULL, registrar); return 0; } 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 = ast_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=""; pri = ast_skip_blanks(pri); pri = ast_trim_blanks(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); } } } ast_config_destroy(cfg); return 1; } static void append_interface(char *iface, int maxlen, char *add) { int len = strlen(iface); if (strlen(add) + len < maxlen - 2) { if (strlen(iface)) { iface[len] = '&'; strcpy(iface + len + 1, add); } else strcpy(iface, add); } } static void pbx_load_users(void) { struct ast_config *cfg; char *cat, *chan; const char *zapchan; const char *hasexten; char tmp[256]; char iface[256]; char zapcopy[256]; char *c; int len; int hasvoicemail; int start, finish, x; struct ast_context *con = NULL; cfg = ast_config_load("users.conf"); if (!cfg) return; for (cat = ast_category_browse(cfg, NULL); cat ; cat = ast_category_browse(cfg, cat)) { if (!strcasecmp(cat, "general")) continue; iface[0] = '\0'; len = sizeof(iface); if (ast_true(ast_config_option(cfg, cat, "hassip"))) { snprintf(tmp, sizeof(tmp), "SIP/%s", cat); append_interface(iface, sizeof(iface), tmp); } if (ast_true(ast_config_option(cfg, cat, "hasiax"))) { snprintf(tmp, sizeof(tmp), "IAX2/%s", cat); append_interface(iface, sizeof(iface), tmp); } if (ast_true(ast_config_option(cfg, cat, "hash323"))) { snprintf(tmp, sizeof(tmp), "H323/%s", cat); append_interface(iface, sizeof(iface), tmp); } hasexten = ast_config_option(cfg, cat, "hasexten"); if (hasexten && !ast_true(hasexten)) continue; hasvoicemail = ast_true(ast_config_option(cfg, cat, "hasvoicemail")); zapchan = ast_variable_retrieve(cfg, cat, "zapchan"); if (!zapchan) zapchan = ast_variable_retrieve(cfg, "general", "zapchan"); if (!ast_strlen_zero(zapchan)) { ast_copy_string(zapcopy, zapchan, sizeof(zapcopy)); c = zapcopy; chan = strsep(&c, ","); while (chan) { if (sscanf(chan, "%d-%d", &start, &finish) == 2) { /* Range */ } else if (sscanf(chan, "%d", &start)) { /* Just one */ finish = start; } else { start = 0; finish = 0; } if (finish < start) { x = finish; finish = start; start = x; } for (x = start; x <= finish; x++) { snprintf(tmp, sizeof(tmp), "Zap/%d", x); append_interface(iface, sizeof(iface), tmp); } chan = strsep(&c, ","); } } if (!ast_strlen_zero(iface)) { /* Only create a context here when it is really needed. Otherwise default empty context created by pbx_config may conflict with the one explicitly created by pbx_ael */ if (!con) con = ast_context_find_or_create(&local_contexts, userscontext, registrar); if (!con) { ast_log(LOG_ERROR, "Can't find/create user context '%s'\n", userscontext); return; } /* Add hint */ ast_add_extension2(con, 0, cat, -1, NULL, NULL, iface, NULL, NULL, registrar); /* If voicemail, use "stdexten" else use plain old dial */ if (hasvoicemail) { snprintf(tmp, sizeof(tmp), "stdexten|%s|${HINT}", cat); ast_add_extension2(con, 0, cat, 1, NULL, NULL, "Macro", strdup(tmp), ast_free, registrar); } else { ast_add_extension2(con, 0, cat, 1, NULL, NULL, "Dial", strdup("${HINT}"), ast_free, registrar); } } } ast_config_destroy(cfg); } static int pbx_load_module(void) { struct ast_context *con; if(!pbx_load_config(config)) return AST_MODULE_LOAD_DECLINE; pbx_load_users(); ast_merge_contexts_and_delete(&local_contexts, registrar); for (con = NULL; (con = ast_walk_contexts(con));) ast_context_verify_includes(con); pbx_set_autofallthrough(autofallthrough_config); return 0; } static int load_module(void) { if (pbx_load_module()) return AST_MODULE_LOAD_DECLINE; if (static_config && !write_protect_config) ast_cli_register(&cli_dialplan_save); ast_cli_register_multiple(cli_pbx_config, sizeof(cli_pbx_config) / sizeof(struct ast_cli_entry)); return 0; } static int reload(void) { if (clearglobalvars_config) pbx_builtin_clear_globals(); pbx_load_module(); return 0; } AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Text Extension Configuration", .load = load_module, .unload = unload_module, .reload = reload, ); asterisk-1.4.21.2/pbx/pbx_realtime.c0000644000175000017500000001612110703712563016443 0ustar maniacmaniac/* * Asterisk -- An open source telephony toolkit. * * Copyright (C) 1999 - 2005, Digium, Inc. * * Mark Spencer * * 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 Realtime PBX Module * * \arg See also: \ref AstARA */ #include "asterisk.h" ASTERISK_FILE_VERSION(__FILE__, "$Revision: 85515 $") #include #include #include #include #include #include "asterisk/file.h" #include "asterisk/logger.h" #include "asterisk/channel.h" #include "asterisk/config.h" #include "asterisk/options.h" #include "asterisk/pbx.h" #include "asterisk/module.h" #include "asterisk/frame.h" #include "asterisk/term.h" #include "asterisk/manager.h" #include "asterisk/file.h" #include "asterisk/cli.h" #include "asterisk/lock.h" #include "asterisk/md5.h" #include "asterisk/linkedlists.h" #include "asterisk/chanvars.h" #include "asterisk/sched.h" #include "asterisk/io.h" #include "asterisk/utils.h" #include "asterisk/crypto.h" #include "asterisk/astdb.h" #define MODE_MATCH 0 #define MODE_MATCHMORE 1 #define MODE_CANMATCH 2 #define EXT_DATA_SIZE 256 /* Realtime switch looks up extensions in the supplied realtime table. [context@][realtimetable][/options] If the realtimetable is omitted it is assumed to be "extensions". If no context is specified the context is assumed to be whatever is the container. The realtime table should have entries for context,exten,priority,app,args The realtime table currently does not support callerid fields. */ static struct ast_variable *realtime_switch_common(const char *table, const char *context, const char *exten, int priority, int mode) { struct ast_variable *var; struct ast_config *cfg; char pri[20]; char *ematch; char rexten[AST_MAX_EXTENSION + 20]=""; int match; snprintf(pri, sizeof(pri), "%d", priority); switch(mode) { case MODE_MATCHMORE: ematch = "exten LIKE"; snprintf(rexten, sizeof(rexten), "%s_%%", exten); break; case MODE_CANMATCH: ematch = "exten LIKE"; snprintf(rexten, sizeof(rexten), "%s%%", exten); break; case MODE_MATCH: default: ematch = "exten"; ast_copy_string(rexten, exten, sizeof(rexten)); } var = ast_load_realtime(table, ematch, rexten, "context", context, "priority", pri, NULL); if (!var) { cfg = ast_load_realtime_multientry(table, "exten LIKE", "\\_%", "context", context, "priority", pri, NULL); if (cfg) { char *cat = ast_category_browse(cfg, NULL); while(cat) { switch(mode) { case MODE_MATCHMORE: match = ast_extension_close(cat, exten, 1); break; case MODE_CANMATCH: match = ast_extension_close(cat, exten, 0); break; case MODE_MATCH: default: match = ast_extension_match(cat, exten); } if (match) { var = ast_category_detach_variables(ast_category_get(cfg, cat)); break; } cat = ast_category_browse(cfg, cat); } ast_config_destroy(cfg); } } return var; } static struct ast_variable *realtime_common(const char *context, const char *exten, int priority, const char *data, int mode) { const char *ctx = NULL; char *table; struct ast_variable *var=NULL; char *buf = ast_strdupa(data); if (buf) { char *opts = strchr(buf, '/'); if (opts) *opts++ = '\0'; table = strchr(buf, '@'); if (table) { *table++ = '\0'; ctx = buf; } ctx = S_OR(ctx, context); table = S_OR(table, "extensions"); var = realtime_switch_common(table, ctx, exten, priority, mode); } return var; } static int realtime_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data) { struct ast_variable *var = realtime_common(context, exten, priority, data, MODE_MATCH); if (var) { ast_variables_destroy(var); return 1; } return 0; } static int realtime_canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data) { struct ast_variable *var = realtime_common(context, exten, priority, data, MODE_CANMATCH); if (var) { ast_variables_destroy(var); return 1; } return 0; } static int realtime_exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data) { int res = -1; struct ast_variable *var = realtime_common(context, exten, priority, data, MODE_MATCH); if (var) { char *tmp=""; char *app = NULL; struct ast_variable *v; for (v = var; v ; v = v->next) { if (!strcasecmp(v->name, "app")) app = ast_strdupa(v->value); else if (!strcasecmp(v->name, "appdata")) tmp = ast_strdupa(v->value); } ast_variables_destroy(var); if (!ast_strlen_zero(app)) { struct ast_app *a = pbx_findapp(app); if (a) { char appdata[512]=""; char tmp1[80]; char tmp2[80]; char tmp3[EXT_DATA_SIZE]; if(!ast_strlen_zero(tmp)) pbx_substitute_variables_helper(chan, tmp, appdata, sizeof(appdata) - 1); if (option_verbose > 2) ast_verbose( VERBOSE_PREFIX_3 "Executing %s(\"%s\", \"%s\")\n", term_color(tmp1, app, COLOR_BRCYAN, 0, sizeof(tmp1)), term_color(tmp2, chan->name, COLOR_BRMAGENTA, 0, sizeof(tmp2)), term_color(tmp3, S_OR(appdata, ""), COLOR_BRMAGENTA, 0, sizeof(tmp3))); manager_event(EVENT_FLAG_CALL, "Newexten", "Channel: %s\r\n" "Context: %s\r\n" "Extension: %s\r\n" "Priority: %d\r\n" "Application: %s\r\n" "AppData: %s\r\n" "Uniqueid: %s\r\n", chan->name, chan->context, chan->exten, chan->priority, app, !ast_strlen_zero(appdata) ? appdata : "(NULL)", chan->uniqueid); res = pbx_exec(chan, a, appdata); } else ast_log(LOG_NOTICE, "No such application '%s' for extension '%s' in context '%s'\n", app, exten, context); } else { ast_log(LOG_WARNING, "No application specified for realtime extension '%s' in context '%s'\n", exten, context); } } return res; } static int realtime_matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data) { struct ast_variable *var = realtime_common(context, exten, priority, data, MODE_MATCHMORE); if (var) { ast_variables_destroy(var); return 1; } return 0; } static struct ast_switch realtime_switch = { name: "Realtime", description: "Realtime Dialplan Switch", exists: realtime_exists, canmatch: realtime_canmatch, exec: realtime_exec, matchmore: realtime_matchmore, }; static int unload_module(void) { ast_unregister_switch(&realtime_switch); return 0; } static int load_module(void) { ast_register_switch(&realtime_switch); return 0; } AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Realtime Switch"); asterisk-1.4.21.2/pbx/pbx_spool.c0000644000175000017500000003502311015066645015777 0ustar maniacmaniac/* * Asterisk -- An open source telephony toolkit. * * Copyright (C) 1999 - 2005, Digium, Inc. * * Mark Spencer * * 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 Full-featured outgoing call spool support * */ #include "asterisk.h" ASTERISK_FILE_VERSION(__FILE__, "$Revision: 117523 $") #include #include #include #include #include #include #include #include #include #include #include #include "asterisk/lock.h" #include "asterisk/file.h" #include "asterisk/logger.h" #include "asterisk/channel.h" #include "asterisk/callerid.h" #include "asterisk/pbx.h" #include "asterisk/module.h" #include "asterisk/options.h" #include "asterisk/utils.h" /* * pbx_spool is similar in spirit to qcall, but with substantially enhanced functionality... * The spool file contains a header */ enum { /*! Always delete the call file after a call succeeds or the * maximum number of retries is exceeded, even if the * modification time of the call file is in the future. */ SPOOL_FLAG_ALWAYS_DELETE = (1 << 0), /* Don't unlink the call file after processing, move in qdonedir */ SPOOL_FLAG_ARCHIVE = (1 << 1) }; static char qdir[255]; static char qdonedir[255]; struct outgoing { char fn[256]; /* Current number of retries */ int retries; /* Maximum number of retries permitted */ int maxretries; /* How long to wait between retries (in seconds) */ int retrytime; /* How long to wait for an answer */ int waittime; /* PID which is currently calling */ long callingpid; /* What to connect to outgoing */ char tech[256]; char dest[256]; /* If application */ char app[256]; char data[256]; /* If extension/context/priority */ char exten[256]; char context[256]; int priority; /* CallerID Information */ char cid_num[256]; char cid_name[256]; /* account code */ char account[AST_MAX_ACCOUNT_CODE]; /* Variables and Functions */ struct ast_variable *vars; /* Maximum length of call */ int maxlen; /* options */ struct ast_flags options; }; static void init_outgoing(struct outgoing *o) { memset(o, 0, sizeof(struct outgoing)); o->priority = 1; o->retrytime = 300; o->waittime = 45; ast_set_flag(&o->options, SPOOL_FLAG_ALWAYS_DELETE); } static void free_outgoing(struct outgoing *o) { free(o); } static int apply_outgoing(struct outgoing *o, char *fn, FILE *f) { char buf[256]; char *c, *c2; int lineno = 0; struct ast_variable *var; while(fgets(buf, sizeof(buf), f)) { lineno++; /* Trim comments */ c = buf; while ((c = strchr(c, '#'))) { if ((c == buf) || (*(c-1) == ' ') || (*(c-1) == '\t')) *c = '\0'; else c++; } c = buf; while ((c = strchr(c, ';'))) { if ((c > buf) && (c[-1] == '\\')) { memmove(c - 1, c, strlen(c) + 1); c++; } else { *c = '\0'; break; } } /* Trim trailing white space */ while(!ast_strlen_zero(buf) && buf[strlen(buf) - 1] < 33) buf[strlen(buf) - 1] = '\0'; if (!ast_strlen_zero(buf)) { c = strchr(buf, ':'); if (c) { *c = '\0'; c++; while ((*c) && (*c < 33)) c++; #if 0 printf("'%s' is '%s' at line %d\n", buf, c, lineno); #endif if (!strcasecmp(buf, "channel")) { ast_copy_string(o->tech, c, sizeof(o->tech)); if ((c2 = strchr(o->tech, '/'))) { *c2 = '\0'; c2++; ast_copy_string(o->dest, c2, sizeof(o->dest)); } else { ast_log(LOG_NOTICE, "Channel should be in form Tech/Dest at line %d of %s\n", lineno, fn); o->tech[0] = '\0'; } } else if (!strcasecmp(buf, "callerid")) { ast_callerid_split(c, o->cid_name, sizeof(o->cid_name), o->cid_num, sizeof(o->cid_num)); } else if (!strcasecmp(buf, "application")) { ast_copy_string(o->app, c, sizeof(o->app)); } else if (!strcasecmp(buf, "data")) { ast_copy_string(o->data, c, sizeof(o->data)); } else if (!strcasecmp(buf, "maxretries")) { if (sscanf(c, "%d", &o->maxretries) != 1) { ast_log(LOG_WARNING, "Invalid max retries at line %d of %s\n", lineno, fn); o->maxretries = 0; } } else if (!strcasecmp(buf, "context")) { ast_copy_string(o->context, c, sizeof(o->context)); } else if (!strcasecmp(buf, "extension")) { ast_copy_string(o->exten, c, sizeof(o->exten)); } else if (!strcasecmp(buf, "priority")) { if ((sscanf(c, "%d", &o->priority) != 1) || (o->priority < 1)) { ast_log(LOG_WARNING, "Invalid priority at line %d of %s\n", lineno, fn); o->priority = 1; } } else if (!strcasecmp(buf, "retrytime")) { if ((sscanf(c, "%d", &o->retrytime) != 1) || (o->retrytime < 1)) { ast_log(LOG_WARNING, "Invalid retrytime at line %d of %s\n", lineno, fn); o->retrytime = 300; } } else if (!strcasecmp(buf, "waittime")) { if ((sscanf(c, "%d", &o->waittime) != 1) || (o->waittime < 1)) { ast_log(LOG_WARNING, "Invalid waittime at line %d of %s\n", lineno, fn); o->waittime = 45; } } else if (!strcasecmp(buf, "retry")) { o->retries++; } else if (!strcasecmp(buf, "startretry")) { if (sscanf(c, "%ld", &o->callingpid) != 1) { ast_log(LOG_WARNING, "Unable to retrieve calling PID!\n"); o->callingpid = 0; } } else if (!strcasecmp(buf, "endretry") || !strcasecmp(buf, "abortretry")) { o->callingpid = 0; o->retries++; } else if (!strcasecmp(buf, "delayedretry")) { } else if (!strcasecmp(buf, "setvar") || !strcasecmp(buf, "set")) { c2 = c; strsep(&c2, "="); if (c2) { var = ast_variable_new(c, c2); if (var) { var->next = o->vars; o->vars = var; } } else ast_log(LOG_WARNING, "Malformed \"%s\" argument. Should be \"%s: variable=value\"\n", buf, buf); } else if (!strcasecmp(buf, "account")) { ast_copy_string(o->account, c, sizeof(o->account)); } else if (!strcasecmp(buf, "alwaysdelete")) { ast_set2_flag(&o->options, ast_true(c), SPOOL_FLAG_ALWAYS_DELETE); } else if (!strcasecmp(buf, "archive")) { ast_set2_flag(&o->options, ast_true(c), SPOOL_FLAG_ARCHIVE); } else { ast_log(LOG_WARNING, "Unknown keyword '%s' at line %d of %s\n", buf, lineno, fn); } } else ast_log(LOG_NOTICE, "Syntax error at line %d of %s\n", lineno, fn); } } ast_copy_string(o->fn, fn, sizeof(o->fn)); if (ast_strlen_zero(o->tech) || ast_strlen_zero(o->dest) || (ast_strlen_zero(o->app) && ast_strlen_zero(o->exten))) { ast_log(LOG_WARNING, "At least one of app or extension must be specified, along with tech and dest in file %s\n", fn); return -1; } return 0; } static void safe_append(struct outgoing *o, time_t now, char *s) { int fd; FILE *f; struct utimbuf tbuf; fd = open(o->fn, O_WRONLY|O_APPEND); if (fd > -1) { f = fdopen(fd, "a"); if (f) { fprintf(f, "\n%s: %ld %d (%ld)\n", s, (long)ast_mainpid, o->retries, (long) now); fclose(f); } else close(fd); /* Update the file time */ tbuf.actime = now; tbuf.modtime = now + o->retrytime; if (utime(o->fn, &tbuf)) ast_log(LOG_WARNING, "Unable to set utime on %s: %s\n", o->fn, strerror(errno)); } } /*! * \brief Remove a call file from the outgoing queue optionally moving it in the archive dir * * \param o the pointer to outgoing struct * \param status the exit status of the call. Can be "Completed", "Failed" or "Expired" */ static int remove_from_queue(struct outgoing *o, const char *status) { int fd; FILE *f; char newfn[256]; const char *bname; if (!ast_test_flag(&o->options, SPOOL_FLAG_ALWAYS_DELETE)) { struct stat current_file_status; if (!stat(o->fn, ¤t_file_status)) if (time(NULL) < current_file_status.st_mtime) return 0; } if (!ast_test_flag(&o->options, SPOOL_FLAG_ARCHIVE)) { unlink(o->fn); return 0; } if (mkdir(qdonedir, 0700) && (errno != EEXIST)) { ast_log(LOG_WARNING, "Unable to create queue directory %s -- outgoing spool archiving disabled\n", qdonedir); unlink(o->fn); return -1; } fd = open(o->fn, O_WRONLY|O_APPEND); if (fd > -1) { f = fdopen(fd, "a"); if (f) { fprintf(f, "Status: %s\n", status); fclose(f); } else close(fd); } bname = strrchr(o->fn,'/'); if (bname == NULL) bname = o->fn; else bname++; snprintf(newfn, sizeof(newfn), "%s/%s", qdonedir, bname); /* a existing call file the archive dir is overwritten */ unlink(newfn); if (rename(o->fn, newfn) != 0) { unlink(o->fn); return -1; } else return 0; } static void *attempt_thread(void *data) { struct outgoing *o = data; int res, reason; if (!ast_strlen_zero(o->app)) { if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Attempting call on %s/%s for application %s(%s) (Retry %d)\n", o->tech, o->dest, o->app, o->data, o->retries); res = ast_pbx_outgoing_app(o->tech, AST_FORMAT_SLINEAR, o->dest, o->waittime * 1000, o->app, o->data, &reason, 2 /* wait to finish */, o->cid_num, o->cid_name, o->vars, o->account, NULL); } else { if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Attempting call on %s/%s for %s@%s:%d (Retry %d)\n", o->tech, o->dest, o->exten, o->context,o->priority, o->retries); res = ast_pbx_outgoing_exten(o->tech, AST_FORMAT_SLINEAR, o->dest, o->waittime * 1000, o->context, o->exten, o->priority, &reason, 2 /* wait to finish */, o->cid_num, o->cid_name, o->vars, o->account, NULL); } if (res) { ast_log(LOG_NOTICE, "Call failed to go through, reason (%d) %s\n", reason, ast_channel_reason2str(reason)); if (o->retries >= o->maxretries + 1) { /* Max retries exceeded */ ast_log(LOG_EVENT, "Queued call to %s/%s expired without completion after %d attempt%s\n", o->tech, o->dest, o->retries - 1, ((o->retries - 1) != 1) ? "s" : ""); remove_from_queue(o, "Expired"); } else { /* Notate that the call is still active */ safe_append(o, time(NULL), "EndRetry"); } } else { ast_log(LOG_NOTICE, "Call completed to %s/%s\n", o->tech, o->dest); ast_log(LOG_EVENT, "Queued call to %s/%s completed\n", o->tech, o->dest); remove_from_queue(o, "Completed"); } free_outgoing(o); return NULL; } static void launch_service(struct outgoing *o) { pthread_t t; pthread_attr_t attr; int ret; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); if ((ret = ast_pthread_create(&t,&attr,attempt_thread, o)) != 0) { ast_log(LOG_WARNING, "Unable to create thread :( (returned error: %d)\n", ret); free_outgoing(o); } pthread_attr_destroy(&attr); } static int scan_service(char *fn, time_t now, time_t atime) { struct outgoing *o; FILE *f; o = malloc(sizeof(struct outgoing)); if (o) { init_outgoing(o); f = fopen(fn, "r+"); if (f) { if (!apply_outgoing(o, fn, f)) { #if 0 printf("Filename: %s, Retries: %d, max: %d\n", fn, o->retries, o->maxretries); #endif fclose(f); if (o->retries <= o->maxretries) { now += o->retrytime; if (o->callingpid && (o->callingpid == ast_mainpid)) { safe_append(o, time(NULL), "DelayedRetry"); ast_log(LOG_DEBUG, "Delaying retry since we're currently running '%s'\n", o->fn); free_outgoing(o); } else { /* Increment retries */ o->retries++; /* If someone else was calling, they're presumably gone now so abort their retry and continue as we were... */ if (o->callingpid) safe_append(o, time(NULL), "AbortRetry"); safe_append(o, now, "StartRetry"); launch_service(o); } return now; } else { ast_log(LOG_EVENT, "Queued call to %s/%s expired without completion after %d attempt%s\n", o->tech, o->dest, o->retries - 1, ((o->retries - 1) != 1) ? "s" : ""); free_outgoing(o); remove_from_queue(o, "Expired"); return 0; } } else { free_outgoing(o); ast_log(LOG_WARNING, "Invalid file contents in %s, deleting\n", fn); fclose(f); remove_from_queue(o, "Failed"); } } else { free_outgoing(o); ast_log(LOG_WARNING, "Unable to open %s: %s, deleting\n", fn, strerror(errno)); remove_from_queue(o, "Failed"); } } else ast_log(LOG_WARNING, "Out of memory :(\n"); return -1; } static void *scan_thread(void *unused) { struct stat st; DIR *dir; struct dirent *de; char fn[256]; int res; time_t last = 0, next = 0, now; for(;;) { /* Wait a sec */ sleep(1); time(&now); if (!stat(qdir, &st)) { if ((st.st_mtime != last) || (next && (now > next))) { #if 0 printf("atime: %ld, mtime: %ld, ctime: %ld\n", st.st_atime, st.st_mtime, st.st_ctime); printf("Ooh, something changed / timeout\n"); #endif next = 0; last = st.st_mtime; dir = opendir(qdir); if (dir) { while((de = readdir(dir))) { snprintf(fn, sizeof(fn), "%s/%s", qdir, de->d_name); if (!stat(fn, &st)) { if (S_ISREG(st.st_mode)) { if (st.st_mtime <= now) { res = scan_service(fn, now, st.st_atime); if (res > 0) { /* Update next service time */ if (!next || (res < next)) { next = res; } } else if (res) ast_log(LOG_WARNING, "Failed to scan service '%s'\n", fn); } else { /* Update "next" update if necessary */ if (!next || (st.st_mtime < next)) next = st.st_mtime; } } } else ast_log(LOG_WARNING, "Unable to stat %s: %s\n", fn, strerror(errno)); } closedir(dir); } else ast_log(LOG_WARNING, "Unable to open directory %s: %s\n", qdir, strerror(errno)); } } else ast_log(LOG_WARNING, "Unable to stat %s\n", qdir); } return NULL; } static int unload_module(void) { return -1; } static int load_module(void) { pthread_t thread; pthread_attr_t attr; int ret; snprintf(qdir, sizeof(qdir), "%s/%s", ast_config_AST_SPOOL_DIR, "outgoing"); if (mkdir(qdir, 0700) && (errno != EEXIST)) { ast_log(LOG_WARNING, "Unable to create queue directory %s -- outgoing spool disabled\n", qdir); return 0; } snprintf(qdonedir, sizeof(qdir), "%s/%s", ast_config_AST_SPOOL_DIR, "outgoing_done"); pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); if ((ret = ast_pthread_create_background(&thread,&attr,scan_thread, NULL)) != 0) { ast_log(LOG_WARNING, "Unable to create thread :( (returned error: %d)\n", ret); return -1; } pthread_attr_destroy(&attr); return 0; } AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Outgoing Spool Support"); asterisk-1.4.21.2/pbx/dundi-parser.h0000644000175000017500000000643710374426007016402 0ustar maniacmaniac/* * Distributed Universal Number Discovery (DUNDi) * * Copyright (C) 2004 - 2005, Digium Inc. * * Written by Mark Spencer * * This program is Free Software distributed under the terms of * of the GNU General Public License. */ #ifndef _DUNDI_PARSER_H #define _DUNDI_PARSER_H #include "asterisk/dundi.h" #include "asterisk/aes.h" #define DUNDI_MAX_STACK 512 #define DUNDI_MAX_ANSWERS 100 struct dundi_ies { dundi_eid *eids[DUNDI_MAX_STACK + 1]; int eid_direct[DUNDI_MAX_STACK + 1]; dundi_eid *reqeid; int eidcount; char *called_context; char *called_number; struct dundi_answer *answers[DUNDI_MAX_ANSWERS + 1]; struct dundi_hint *hint; int anscount; int ttl; int version; int expiration; int unknowncmd; unsigned char *pubkey; int cause; char *q_dept; char *q_org; char *q_locality; char *q_stateprov; char *q_country; char *q_email; char *q_phone; char *q_ipaddr; char *causestr; unsigned char *encsharedkey; unsigned char *encsig; unsigned long keycrc32; struct dundi_encblock *encblock; int enclen; int cbypass; }; struct dundi_ie_data { int pos; unsigned char buf[8192]; }; /* Choose a different function for output */ extern void dundi_set_output(void (*output)(const char *data)); /* Choose a different function for errors */ extern void dundi_set_error(void (*output)(const char *data)); extern void dundi_showframe(struct dundi_hdr *fhi, int rx, struct sockaddr_in *sin, int datalen); extern const char *dundi_ie2str(int ie); extern int dundi_ie_append_raw(struct dundi_ie_data *ied, unsigned char ie, void *data, int datalen); extern int dundi_ie_append_addr(struct dundi_ie_data *ied, unsigned char ie, struct sockaddr_in *sin); extern int dundi_ie_append_int(struct dundi_ie_data *ied, unsigned char ie, unsigned int value); extern int dundi_ie_append_short(struct dundi_ie_data *ied, unsigned char ie, unsigned short value); extern int dundi_ie_append_str(struct dundi_ie_data *ied, unsigned char ie, char *str); extern int dundi_ie_append_eid(struct dundi_ie_data *ied, unsigned char ie, dundi_eid *eid); extern int dundi_ie_append_cause(struct dundi_ie_data *ied, unsigned char ie, unsigned char cause, char *desc); extern int dundi_ie_append_hint(struct dundi_ie_data *ied, unsigned char ie, unsigned short flags, char *data); extern int dundi_ie_append_answer(struct dundi_ie_data *ied, unsigned char ie, dundi_eid *eid, unsigned char protocol, unsigned short flags, unsigned short weight, char *desc); extern int dundi_ie_append_encdata(struct dundi_ie_data *ied, unsigned char ie, unsigned char *iv, void *data, int datalen); extern int dundi_ie_append_byte(struct dundi_ie_data *ied, unsigned char ie, unsigned char dat); extern int dundi_ie_append(struct dundi_ie_data *ied, unsigned char ie); extern int dundi_parse_ies(struct dundi_ies *ies, unsigned char *data, int datalen); extern char *dundi_eid_to_str(char *s, int maxlen, dundi_eid *eid); extern char *dundi_eid_to_str_short(char *s, int maxlen, dundi_eid *eid); extern int dundi_str_to_eid(dundi_eid *eid, char *s); extern int dundi_str_short_to_eid(dundi_eid *eid, char *s); extern int dundi_eid_zero(dundi_eid *eid); extern int dundi_eid_cmp(dundi_eid *eid1, dundi_eid *eid2); extern char *dundi_flags2str(char *s, int maxlen, int flags); extern char *dundi_hint2str(char *s, int maxlen, int flags); #endif asterisk-1.4.21.2/pbx/dundi-parser.c0000644000175000017500000005510710517672550016400 0ustar maniacmaniac/* * Asterisk -- An open source telephony toolkit. * * Copyright (C) 1999 - 2005, Digium, Inc. * * Mark Spencer * * 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 Distributed Universal Number Discovery (DUNDi) * */ #include "asterisk.h" ASTERISK_FILE_VERSION(__FILE__, "$Revision: 46200 $") #include #include #include #include #include #include #include #include #include "asterisk/frame.h" #include "asterisk/utils.h" #include "asterisk/dundi.h" #include "dundi-parser.h" #include "asterisk/dundi.h" static void internaloutput(const char *str) { fputs(str, stdout); } static void internalerror(const char *str) { fprintf(stderr, "WARNING: %s", str); } static void (*outputf)(const char *str) = internaloutput; static void (*errorf)(const char *str) = internalerror; char *dundi_eid_to_str(char *s, int maxlen, dundi_eid *eid) { int x; char *os = s; if (maxlen < 18) { if (s && (maxlen > 0)) *s = '\0'; } else { for (x=0;x<5;x++) { sprintf(s, "%02x:", eid->eid[x]); s += 3; } sprintf(s, "%02x", eid->eid[5]); } return os; } char *dundi_eid_to_str_short(char *s, int maxlen, dundi_eid *eid) { int x; char *os = s; if (maxlen < 13) { if (s && (maxlen > 0)) *s = '\0'; } else { for (x=0;x<6;x++) { sprintf(s, "%02X", eid->eid[x]); s += 2; } } return os; } int dundi_str_to_eid(dundi_eid *eid, char *s) { unsigned int eid_int[6]; int x; if (sscanf(s, "%x:%x:%x:%x:%x:%x", &eid_int[0], &eid_int[1], &eid_int[2], &eid_int[3], &eid_int[4], &eid_int[5]) != 6) return -1; for (x=0;x<6;x++) eid->eid[x] = eid_int[x]; return 0; } int dundi_str_short_to_eid(dundi_eid *eid, char *s) { unsigned int eid_int[6]; int x; if (sscanf(s, "%2x%2x%2x%2x%2x%2x", &eid_int[0], &eid_int[1], &eid_int[2], &eid_int[3], &eid_int[4], &eid_int[5]) != 6) return -1; for (x=0;x<6;x++) eid->eid[x] = eid_int[x]; return 0; } int dundi_eid_zero(dundi_eid *eid) { int x; for (x=0;xeid) / sizeof(eid->eid[0]);x++) if (eid->eid[x]) return 0; return 1; } int dundi_eid_cmp(dundi_eid *eid1, dundi_eid *eid2) { return memcmp(eid1, eid2, sizeof(dundi_eid)); } static void dump_string(char *output, int maxlen, void *value, int len) { maxlen--; if (maxlen > len) maxlen = len; strncpy(output, value, maxlen); output[maxlen] = '\0'; } static void dump_cbypass(char *output, int maxlen, void *value, int len) { maxlen--; strncpy(output, "Bypass Caches", maxlen); output[maxlen] = '\0'; } static void dump_eid(char *output, int maxlen, void *value, int len) { if (len == 6) dundi_eid_to_str(output, maxlen, (dundi_eid *)value); else snprintf(output, maxlen, "Invalid EID len %d", len); } char *dundi_hint2str(char *buf, int bufsiz, int flags) { strcpy(buf, ""); buf[bufsiz-1] = '\0'; if (flags & DUNDI_HINT_TTL_EXPIRED) { strncat(buf, "TTLEXPIRED|", bufsiz - strlen(buf) - 1); } if (flags & DUNDI_HINT_DONT_ASK) { strncat(buf, "DONTASK|", bufsiz - strlen(buf) - 1); } if (flags & DUNDI_HINT_UNAFFECTED) { strncat(buf, "UNAFFECTED|", bufsiz - strlen(buf) - 1); } /* Get rid of trailing | */ if (ast_strlen_zero(buf)) strcpy(buf, "NONE|"); buf[strlen(buf)-1] = '\0'; return buf; } static void dump_hint(char *output, int maxlen, void *value, int len) { unsigned short flags; char tmp[512]; char tmp2[256]; if (len < 2) { strncpy(output, "", maxlen); return; } memcpy(&flags, value, sizeof(flags)); flags = ntohs(flags); memset(tmp, 0, sizeof(tmp)); dundi_hint2str(tmp2, sizeof(tmp2), flags); snprintf(tmp, sizeof(tmp), "[%s] ", tmp2); memcpy(tmp + strlen(tmp), value + 2, len - 2); strncpy(output, tmp, maxlen - 1); } static void dump_cause(char *output, int maxlen, void *value, int len) { static char *causes[] = { "SUCCESS", "GENERAL", "DYNAMIC", "NOAUTH" , }; char tmp[256]; char tmp2[256]; int mlen; unsigned char cause; if (len < 1) { strncpy(output, "", maxlen); return; } cause = *((unsigned char *)value); memset(tmp2, 0, sizeof(tmp2)); mlen = len - 1; if (mlen > 255) mlen = 255; memcpy(tmp2, value + 1, mlen); if (cause < sizeof(causes) / sizeof(causes[0])) { if (len > 1) snprintf(tmp, sizeof(tmp), "%s: %s", causes[cause], tmp2); else snprintf(tmp, sizeof(tmp), "%s", causes[cause]); } else { if (len > 1) snprintf(tmp, sizeof(tmp), "%d: %s", cause, tmp2); else snprintf(tmp, sizeof(tmp), "%d", cause); } strncpy(output,tmp, maxlen); output[maxlen] = '\0'; } static void dump_int(char *output, int maxlen, void *value, int len) { if (len == (int)sizeof(unsigned int)) snprintf(output, maxlen, "%lu", (unsigned long)ntohl(*((unsigned int *)value))); else snprintf(output, maxlen, "Invalid INT"); } static void dump_short(char *output, int maxlen, void *value, int len) { if (len == (int)sizeof(unsigned short)) snprintf(output, maxlen, "%d", ntohs(*((unsigned short *)value))); else snprintf(output, maxlen, "Invalid SHORT"); } static void dump_byte(char *output, int maxlen, void *value, int len) { if (len == (int)sizeof(unsigned char)) snprintf(output, maxlen, "%d", *((unsigned char *)value)); else snprintf(output, maxlen, "Invalid BYTE"); } static char *proto2str(int proto, char *buf, int bufsiz) { switch(proto) { case DUNDI_PROTO_NONE: strncpy(buf, "None", bufsiz - 1); break; case DUNDI_PROTO_IAX: strncpy(buf, "IAX", bufsiz - 1); break; case DUNDI_PROTO_SIP: strncpy(buf, "SIP", bufsiz - 1); break; case DUNDI_PROTO_H323: strncpy(buf, "H.323", bufsiz - 1); break; default: snprintf(buf, bufsiz, "Unknown Proto(%d)", proto); } buf[bufsiz-1] = '\0'; return buf; } char *dundi_flags2str(char *buf, int bufsiz, int flags) { strcpy(buf, ""); buf[bufsiz-1] = '\0'; if (flags & DUNDI_FLAG_EXISTS) { strncat(buf, "EXISTS|", bufsiz - strlen(buf) - 1); } if (flags & DUNDI_FLAG_MATCHMORE) { strncat(buf, "MATCHMORE|", bufsiz - strlen(buf) - 1); } if (flags & DUNDI_FLAG_CANMATCH) { strncat(buf, "CANMATCH|", bufsiz - strlen(buf) - 1); } if (flags & DUNDI_FLAG_IGNOREPAT) { strncat(buf, "IGNOREPAT|", bufsiz - strlen(buf) - 1); } if (flags & DUNDI_FLAG_RESIDENTIAL) { strncat(buf, "RESIDENCE|", bufsiz - strlen(buf) - 1); } if (flags & DUNDI_FLAG_COMMERCIAL) { strncat(buf, "COMMERCIAL|", bufsiz - strlen(buf) - 1); } if (flags & DUNDI_FLAG_MOBILE) { strncat(buf, "MOBILE", bufsiz - strlen(buf) - 1); } if (flags & DUNDI_FLAG_NOUNSOLICITED) { strncat(buf, "NOUNSLCTD|", bufsiz - strlen(buf) - 1); } if (flags & DUNDI_FLAG_NOCOMUNSOLICIT) { strncat(buf, "NOCOMUNSLTD|", bufsiz - strlen(buf) - 1); } /* Get rid of trailing | */ if (ast_strlen_zero(buf)) strcpy(buf, "NONE|"); buf[strlen(buf)-1] = '\0'; return buf; } static void dump_answer(char *output, int maxlen, void *value, int len) { struct dundi_answer *answer; char proto[40]; char flags[40]; char eid_str[40]; char tmp[512]=""; if (len >= 10) { answer = (struct dundi_answer *)(value); memcpy(tmp, answer->data, (len >= 500) ? 500 : len - 10); dundi_eid_to_str(eid_str, sizeof(eid_str), &answer->eid); snprintf(output, maxlen, "[%s] %d <%s/%s> from [%s]", dundi_flags2str(flags, sizeof(flags), ntohs(answer->flags)), ntohs(answer->weight), proto2str(answer->protocol, proto, sizeof(proto)), tmp, eid_str); } else strncpy(output, "Invalid Answer", maxlen - 1); } static void dump_encrypted(char *output, int maxlen, void *value, int len) { char iv[33]; int x; if ((len > 16) && !(len % 16)) { /* Build up IV */ for (x=0;x<16;x++) { snprintf(iv + (x << 1), 3, "%02x", ((unsigned char *)value)[x]); } snprintf(output, maxlen, "[IV %s] %d encrypted blocks\n", iv, len / 16); } else snprintf(output, maxlen, "Invalid Encrypted Datalen %d", len); } static void dump_raw(char *output, int maxlen, void *value, int len) { int x; unsigned char *u = value; output[maxlen - 1] = '\0'; strcpy(output, "[ "); for (x=0;x= 2) { ie = iedata[0]; ielen = iedata[1]; /* Encrypted data is the remainder */ if (ie == DUNDI_IE_ENCDATA) ielen = len - 2; if (ielen + 2> len) { snprintf(tmp, (int)sizeof(tmp), "Total IE length of %d bytes exceeds remaining frame length of %d bytes\n", ielen + 2, len); outputf(tmp); return; } found = 0; for (x=0;x<(int)sizeof(ies) / (int)sizeof(ies[0]); x++) { if (ies[x].ie == ie) { if (ies[x].dump) { ies[x].dump(interp, (int)sizeof(interp), iedata + 2, ielen); snprintf(tmp, (int)sizeof(tmp), " %s%-15.15s : %s\n", (spaces ? " " : "" ), ies[x].name, interp); outputf(tmp); } else { if (ielen) snprintf(interp, (int)sizeof(interp), "%d bytes", ielen); else strcpy(interp, "Present"); snprintf(tmp, (int)sizeof(tmp), " %s%-15.15s : %s\n", (spaces ? " " : "" ), ies[x].name, interp); outputf(tmp); } found++; } } if (!found) { snprintf(tmp, (int)sizeof(tmp), " %sUnknown IE %03d : Present\n", (spaces ? " " : "" ), ie); outputf(tmp); } iedata += (2 + ielen); len -= (2 + ielen); } outputf("\n"); } void dundi_showframe(struct dundi_hdr *fhi, int rx, struct sockaddr_in *sin, int datalen) { char *pref[] = { "Tx", "Rx", " ETx", " Erx" }; char *commands[] = { "ACK ", "DPDISCOVER ", "DPRESPONSE ", "EIDQUERY ", "EIDRESPONSE ", "PRECACHERQ ", "PRECACHERP ", "INVALID ", "UNKNOWN CMD ", "NULL ", "REQREQ ", "REGRESPONSE ", "CANCEL ", "ENCRYPT ", "ENCREJ " }; char class2[20]; char *class; char subclass2[20]; char *subclass; char tmp[256]; char retries[20]; if (ntohs(fhi->dtrans) & DUNDI_FLAG_RETRANS) strcpy(retries, "Yes"); else strcpy(retries, "No"); if ((ntohs(fhi->strans) & DUNDI_FLAG_RESERVED)) { /* Ignore frames with high bit set to 1 */ return; } if ((fhi->cmdresp & 0x3f) > (int)sizeof(commands)/(int)sizeof(char *)) { snprintf(class2, (int)sizeof(class2), "(%d?)", fhi->cmdresp); class = class2; } else { class = commands[(int)(fhi->cmdresp & 0x3f)]; } snprintf(subclass2, (int)sizeof(subclass2), "%02x", fhi->cmdflags); subclass = subclass2; snprintf(tmp, (int)sizeof(tmp), "%s-Frame Retry[%s] -- OSeqno: %3.3d ISeqno: %3.3d Type: %s (%s)\n", pref[rx], retries, fhi->oseqno, fhi->iseqno, class, fhi->cmdresp & 0x40 ? "Response" : "Command"); outputf(tmp); snprintf(tmp, (int)sizeof(tmp), "%s Flags: %s STrans: %5.5d DTrans: %5.5d [%s:%d]%s\n", (rx > 1) ? " " : "", subclass, ntohs(fhi->strans) & ~DUNDI_FLAG_RESERVED, ntohs(fhi->dtrans) & ~DUNDI_FLAG_RETRANS, ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port), fhi->cmdresp & 0x80 ? " (Final)" : ""); outputf(tmp); dump_ies(fhi->ies, rx > 1, datalen); } int dundi_ie_append_raw(struct dundi_ie_data *ied, unsigned char ie, void *data, int datalen) { char tmp[256]; if (datalen > ((int)sizeof(ied->buf) - ied->pos)) { snprintf(tmp, (int)sizeof(tmp), "Out of space for ie '%s' (%d), need %d have %d\n", dundi_ie2str(ie), ie, datalen, (int)sizeof(ied->buf) - ied->pos); errorf(tmp); return -1; } ied->buf[ied->pos++] = ie; ied->buf[ied->pos++] = datalen; memcpy(ied->buf + ied->pos, data, datalen); ied->pos += datalen; return 0; } int dundi_ie_append_cause(struct dundi_ie_data *ied, unsigned char ie, unsigned char cause, char *data) { char tmp[256]; int datalen = data ? strlen(data) + 1 : 1; if (datalen > ((int)sizeof(ied->buf) - ied->pos)) { snprintf(tmp, (int)sizeof(tmp), "Out of space for ie '%s' (%d), need %d have %d\n", dundi_ie2str(ie), ie, datalen, (int)sizeof(ied->buf) - ied->pos); errorf(tmp); return -1; } ied->buf[ied->pos++] = ie; ied->buf[ied->pos++] = datalen; ied->buf[ied->pos++] = cause; memcpy(ied->buf + ied->pos, data, datalen-1); ied->pos += datalen-1; return 0; } int dundi_ie_append_hint(struct dundi_ie_data *ied, unsigned char ie, unsigned short flags, char *data) { char tmp[256]; int datalen = data ? strlen(data) + 2 : 2; if (datalen > ((int)sizeof(ied->buf) - ied->pos)) { snprintf(tmp, (int)sizeof(tmp), "Out of space for ie '%s' (%d), need %d have %d\n", dundi_ie2str(ie), ie, datalen, (int)sizeof(ied->buf) - ied->pos); errorf(tmp); return -1; } ied->buf[ied->pos++] = ie; ied->buf[ied->pos++] = datalen; flags = htons(flags); memcpy(ied->buf + ied->pos, &flags, sizeof(flags)); ied->pos += 2; memcpy(ied->buf + ied->pos, data, datalen-1); ied->pos += datalen-2; return 0; } int dundi_ie_append_encdata(struct dundi_ie_data *ied, unsigned char ie, unsigned char *iv, void *data, int datalen) { char tmp[256]; datalen += 16; if (datalen > ((int)sizeof(ied->buf) - ied->pos)) { snprintf(tmp, (int)sizeof(tmp), "Out of space for ie '%s' (%d), need %d have %d\n", dundi_ie2str(ie), ie, datalen, (int)sizeof(ied->buf) - ied->pos); errorf(tmp); return -1; } ied->buf[ied->pos++] = ie; ied->buf[ied->pos++] = datalen; memcpy(ied->buf + ied->pos, iv, 16); ied->pos += 16; if (data) { memcpy(ied->buf + ied->pos, data, datalen-16); ied->pos += datalen-16; } return 0; } int dundi_ie_append_answer(struct dundi_ie_data *ied, unsigned char ie, dundi_eid *eid, unsigned char protocol, unsigned short flags, unsigned short weight, char *data) { char tmp[256]; int datalen = data ? strlen(data) + 11 : 11; int x; unsigned short myw; if (datalen > ((int)sizeof(ied->buf) - ied->pos)) { snprintf(tmp, (int)sizeof(tmp), "Out of space for ie '%s' (%d), need %d have %d\n", dundi_ie2str(ie), ie, datalen, (int)sizeof(ied->buf) - ied->pos); errorf(tmp); return -1; } ied->buf[ied->pos++] = ie; ied->buf[ied->pos++] = datalen; for (x=0;x<6;x++) ied->buf[ied->pos++] = eid->eid[x]; ied->buf[ied->pos++] = protocol; myw = htons(flags); memcpy(ied->buf + ied->pos, &myw, 2); ied->pos += 2; myw = htons(weight); memcpy(ied->buf + ied->pos, &myw, 2); ied->pos += 2; memcpy(ied->buf + ied->pos, data, datalen-11); ied->pos += datalen-11; return 0; } int dundi_ie_append_addr(struct dundi_ie_data *ied, unsigned char ie, struct sockaddr_in *sin) { return dundi_ie_append_raw(ied, ie, sin, (int)sizeof(struct sockaddr_in)); } int dundi_ie_append_int(struct dundi_ie_data *ied, unsigned char ie, unsigned int value) { unsigned int newval; newval = htonl(value); return dundi_ie_append_raw(ied, ie, &newval, (int)sizeof(newval)); } int dundi_ie_append_short(struct dundi_ie_data *ied, unsigned char ie, unsigned short value) { unsigned short newval; newval = htons(value); return dundi_ie_append_raw(ied, ie, &newval, (int)sizeof(newval)); } int dundi_ie_append_str(struct dundi_ie_data *ied, unsigned char ie, char *str) { return dundi_ie_append_raw(ied, ie, str, strlen(str)); } int dundi_ie_append_eid(struct dundi_ie_data *ied, unsigned char ie, dundi_eid *eid) { return dundi_ie_append_raw(ied, ie, (unsigned char *)eid, sizeof(dundi_eid)); } int dundi_ie_append_byte(struct dundi_ie_data *ied, unsigned char ie, unsigned char dat) { return dundi_ie_append_raw(ied, ie, &dat, 1); } int dundi_ie_append(struct dundi_ie_data *ied, unsigned char ie) { return dundi_ie_append_raw(ied, ie, NULL, 0); } void dundi_set_output(void (*func)(const char *)) { outputf = func; } void dundi_set_error(void (*func)(const char *)) { errorf = func; } int dundi_parse_ies(struct dundi_ies *ies, unsigned char *data, int datalen) { /* Parse data into information elements */ int len; int ie; char tmp[256]; memset(ies, 0, (int)sizeof(struct dundi_ies)); ies->ttl = -1; ies->expiration = -1; ies->unknowncmd = -1; ies->cause = -1; while(datalen >= 2) { ie = data[0]; len = data[1]; if (len > datalen - 2) { errorf("Information element length exceeds message size\n"); return -1; } switch(ie) { case DUNDI_IE_EID: case DUNDI_IE_EID_DIRECT: if (len != (int)sizeof(dundi_eid)) { errorf("Improper entity identifer, expecting 6 bytes!\n"); } else if (ies->eidcount < DUNDI_MAX_STACK) { ies->eids[ies->eidcount] = (dundi_eid *)(data + 2); ies->eid_direct[ies->eidcount] = (ie == DUNDI_IE_EID_DIRECT); ies->eidcount++; } else errorf("Too many entities in stack!\n"); break; case DUNDI_IE_REQEID: if (len != (int)sizeof(dundi_eid)) { errorf("Improper requested entity identifer, expecting 6 bytes!\n"); } else ies->reqeid = (dundi_eid *)(data + 2); break; case DUNDI_IE_CALLED_CONTEXT: ies->called_context = (char *)data + 2; break; case DUNDI_IE_CALLED_NUMBER: ies->called_number = (char *)data + 2; break; case DUNDI_IE_ANSWER: if (len < sizeof(struct dundi_answer)) { snprintf(tmp, (int)sizeof(tmp), "Answer expected to be >=%d bytes long but was %d\n", (int)sizeof(struct dundi_answer), len); errorf(tmp); } else { if (ies->anscount < DUNDI_MAX_ANSWERS) ies->answers[ies->anscount++]= (struct dundi_answer *)(data + 2); else errorf("Ignoring extra answers!\n"); } break; case DUNDI_IE_TTL: if (len != (int)sizeof(unsigned short)) { snprintf(tmp, (int)sizeof(tmp), "Expecting ttl to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len); errorf(tmp); } else ies->ttl = ntohs(*((unsigned short *)(data + 2))); break; case DUNDI_IE_VERSION: if (len != (int)sizeof(unsigned short)) { snprintf(tmp, (int)sizeof(tmp), "Expecting version to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len); errorf(tmp); } else ies->version = ntohs(*((unsigned short *)(data + 2))); break; case DUNDI_IE_EXPIRATION: if (len != (int)sizeof(unsigned short)) { snprintf(tmp, (int)sizeof(tmp), "Expecting expiration to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len); errorf(tmp); } else ies->expiration = ntohs(*((unsigned short *)(data + 2))); break; case DUNDI_IE_KEYCRC32: if (len != (int)sizeof(unsigned int)) { snprintf(tmp, (int)sizeof(tmp), "Expecting expiration to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len); errorf(tmp); } else ies->keycrc32 = ntohl(*((unsigned int *)(data + 2))); break; case DUNDI_IE_UNKNOWN: if (len == 1) ies->unknowncmd = data[2]; else { snprintf(tmp, (int)sizeof(tmp), "Expected single byte Unknown command, but was %d long\n", len); errorf(tmp); } break; case DUNDI_IE_CAUSE: if (len >= 1) { ies->cause = data[2]; ies->causestr = (char *)data + 3; } else { snprintf(tmp, (int)sizeof(tmp), "Expected at least one byte cause, but was %d long\n", len); errorf(tmp); } break; case DUNDI_IE_HINT: if (len >= 2) { ies->hint = (struct dundi_hint *)(data + 2); } else { snprintf(tmp, (int)sizeof(tmp), "Expected at least two byte hint, but was %d long\n", len); errorf(tmp); } break; case DUNDI_IE_DEPARTMENT: ies->q_dept = (char *)data + 2; break; case DUNDI_IE_ORGANIZATION: ies->q_org = (char *)data + 2; break; case DUNDI_IE_LOCALITY: ies->q_locality = (char *)data + 2; break; case DUNDI_IE_STATE_PROV: ies->q_stateprov = (char *)data + 2; break; case DUNDI_IE_COUNTRY: ies->q_country = (char *)data + 2; break; case DUNDI_IE_EMAIL: ies->q_email = (char *)data + 2; break; case DUNDI_IE_PHONE: ies->q_phone = (char *)data + 2; break; case DUNDI_IE_IPADDR: ies->q_ipaddr = (char *)data + 2; break; case DUNDI_IE_ENCDATA: /* Recalculate len as the remainder of the message, regardless of theoretical length */ len = datalen - 2; if ((len > 16) && !(len % 16)) { ies->encblock = (struct dundi_encblock *)(data + 2); ies->enclen = len - 16; } else { snprintf(tmp, (int)sizeof(tmp), "Invalid encrypted data length %d\n", len); errorf(tmp); } break; case DUNDI_IE_SHAREDKEY: if (len == 128) { ies->encsharedkey = (unsigned char *)(data + 2); } else { snprintf(tmp, (int)sizeof(tmp), "Invalid encrypted shared key length %d\n", len); errorf(tmp); } break; case DUNDI_IE_SIGNATURE: if (len == 128) { ies->encsig = (unsigned char *)(data + 2); } else { snprintf(tmp, (int)sizeof(tmp), "Invalid encrypted signature length %d\n", len); errorf(tmp); } break; case DUNDI_IE_CACHEBYPASS: ies->cbypass = 1; break; default: snprintf(tmp, (int)sizeof(tmp), "Ignoring unknown information element '%s' (%d) of length %d\n", dundi_ie2str(ie), ie, len); outputf(tmp); } /* Overwrite information element with 0, to null terminate previous portion */ data[0] = 0; datalen -= (len + 2); data += (len + 2); } /* Null-terminate last field */ *data = '\0'; if (datalen) { errorf("Invalid information element contents, strange boundary\n"); return -1; } return 0; } asterisk-1.4.21.2/pbx/pbx_loopback.c0000644000175000017500000001364211021344200016417 0ustar maniacmaniac/* * Asterisk -- An open source telephony toolkit. * * Copyright (C) 1999 - 2005, Digium, Inc. * * Mark Spencer * * 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 Loopback PBX Module * */ #include "asterisk.h" ASTERISK_FILE_VERSION(__FILE__, "$Revision: 120226 $") #include #include #include #include #include #include "asterisk/file.h" #include "asterisk/logger.h" #include "asterisk/channel.h" #include "asterisk/config.h" #include "asterisk/options.h" #include "asterisk/pbx.h" #include "asterisk/module.h" #include "asterisk/frame.h" #include "asterisk/file.h" #include "asterisk/cli.h" #include "asterisk/lock.h" #include "asterisk/md5.h" #include "asterisk/linkedlists.h" #include "asterisk/chanvars.h" #include "asterisk/sched.h" #include "asterisk/io.h" #include "asterisk/utils.h" #include "asterisk/crypto.h" #include "asterisk/astdb.h" /* Loopback switch creates a 'tunnel' to another context. When extension lookups pass through the 'tunnel', Asterisk expressions can be used to modify the target extension, context, and priority in any way desired. If there is a match at the far end, execution jumps through the 'tunnel' to the matched context, extension, and priority. Global variables as well as ${CONTEXT}, ${EXTEN}, and ${PRIORITY} are available for substitution. After substitution Loopback expects to get a string of the form: [exten]@context[:priority][/extramatch] Where exten, context, and priority are another extension, context, and priority to lookup and "extramatch" is a dialplan extension pattern which the *original* number must match. If exten or priority are empty, the original values are used. Note that the search context MUST be a different context from the current context or the search will not succeed. This is intended to reduce the likelihood of loops (they're still possible if you try hard, so be careful!) */ #define LOOPBACK_COMMON \ char buf[1024]; \ int res; \ char *newexten=(char *)exten, *newcontext=(char *)context; \ int newpriority=priority; \ char *newpattern=NULL; \ loopback_subst(buf, sizeof(buf), exten, context, priority, data); \ loopback_parse(&newexten, &newcontext, &newpriority, &newpattern, buf); \ ast_log(LOG_DEBUG, "Parsed into %s @ %s priority %d\n", newexten, newcontext, newpriority); \ if (!strcasecmp(newcontext, context)) return -1 static char *loopback_subst(char *buf, int buflen, const char *exten, const char *context, int priority, const char *data) { struct ast_var_t *newvariable; struct varshead headp; char tmp[80]; snprintf(tmp, sizeof(tmp), "%d", priority); memset(buf, 0, buflen); AST_LIST_HEAD_INIT_NOLOCK(&headp); newvariable = ast_var_assign("EXTEN", exten); AST_LIST_INSERT_HEAD(&headp, newvariable, entries); newvariable = ast_var_assign("CONTEXT", context); AST_LIST_INSERT_HEAD(&headp, newvariable, entries); newvariable = ast_var_assign("PRIORITY", tmp); AST_LIST_INSERT_HEAD(&headp, newvariable, entries); /* Substitute variables */ pbx_substitute_variables_varshead(&headp, data, buf, buflen); /* free the list */ while ((newvariable = AST_LIST_REMOVE_HEAD(&headp, entries))) ast_var_delete(newvariable); return buf; } static void loopback_parse(char **newexten, char **newcontext, int *priority, char **newpattern, char *buf) { char *con; char *pri; *newpattern = strchr(buf, '/'); if (*newpattern) *(*newpattern)++ = '\0'; con = strchr(buf, '@'); if (con) { *con++ = '\0'; pri = strchr(con, ':'); } else pri = strchr(buf, ':'); if (!ast_strlen_zero(buf)) *newexten = buf; if (!ast_strlen_zero(con)) *newcontext = con; if (!ast_strlen_zero(pri)) sscanf(pri, "%d", priority); } static int loopback_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data) { LOOPBACK_COMMON; res = ast_exists_extension(chan, newcontext, newexten, newpriority, callerid); if (newpattern && !ast_extension_match(newpattern, exten)) res = 0; return res; } static int loopback_canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data) { LOOPBACK_COMMON; res = ast_canmatch_extension(chan, newcontext, newexten, newpriority, callerid); if (newpattern && !ast_extension_match(newpattern, exten)) res = 0; return res; } static int loopback_exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data) { LOOPBACK_COMMON; res = ast_spawn_extension(chan, newcontext, newexten, newpriority, callerid); return res; } static int loopback_matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data) { LOOPBACK_COMMON; res = ast_matchmore_extension(chan, newcontext, newexten, newpriority, callerid); if (newpattern && !ast_extension_match(newpattern, exten)) res = 0; return res; } static struct ast_switch loopback_switch = { name: "Loopback", description: "Loopback Dialplan Switch", exists: loopback_exists, canmatch: loopback_canmatch, exec: loopback_exec, matchmore: loopback_matchmore, }; static int unload_module(void) { ast_unregister_switch(&loopback_switch); return 0; } static int load_module(void) { ast_register_switch(&loopback_switch); return 0; } AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Loopback Switch"); asterisk-1.4.21.2/pbx/ael/0000755000175000017500000000000011041455740014361 5ustar maniacmaniacasterisk-1.4.21.2/pbx/ael/ael-test/0000755000175000017500000000000011041455740016077 5ustar maniacmaniacasterisk-1.4.21.2/pbx/ael/ael-test/ref.ael-test140000644000175000017500000000230210660114702020451 0ustar maniacmaniac (If you find progress and other non-error messages irritating, you can use -q to suppress them) (You can use the -w option to dump extensions.conf format to extensions.conf.aeldump) LOG: lev:2 file:pbx_ael.c line:3978 func: pbx_load_module Starting AEL load process. LOG: lev:2 file:pbx_ael.c line:3985 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:3993 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. LOG: lev:3 file:pbx_ael.c line:2234 func: check_switch_expr Warning: file ./extensions.ael, line 13-13: A default case was automatically added to the switch. LOG: lev:4 file:pbx_ael.c line:1086 func: check_continue Error: file ./extensions.ael, line 15-15: 'continue' not in 'for' or 'while' statement! LOG: lev:4 file:pbx_ael.c line:1067 func: check_break Error: file ./extensions.ael, line 17-17: 'break' not in switch, for, or while statement! LOG: lev:4 file:pbx_ael.c line:4006 func: pbx_load_module Sorry, but 0 syntax errors and 2 semantic errors were detected. It doesn't make sense to compile. LOG: lev:4 file:ael2_parse line:523 func: main 0 contexts, 0 extensions, 0 priorities asterisk-1.4.21.2/pbx/ael/ael-test/ael-vtest17/0000755000175000017500000000000011041455740020153 5ustar maniacmaniacasterisk-1.4.21.2/pbx/ael/ael-test/ael-vtest17/extensions.ael0000644000175000017500000000324410513776201023041 0ustar maniacmaniaccontext dialextens { /* 101 thru 123, 149 thru 152 */ _10X => Dial(Zap/${EXTEN:2},30,tw); _1ZX => Dial(Zap/${EXTEN:1},30,tw); } /* Due to extenal wiring: dialing 125 will ring 101 dialing 126 will ring 102 and so on until dialing 147 will ring 123 We can dial out on zap 69 thru 72; and 25-47 */ context dialthrus { /* 369-372; 325-347 */ _3XX => Dial(Zap/${EXTEN:1},30,tw); } context t1incoming { includes { dialextens; parkedcalls; } s => { Answer(); Background(welcome-to-test-machine); } } context t1extension { includes { dialextens; dialthrus; } } context incoming { includes { dialextens; parkedcalls; } s => { Answer(); Background(welcome-to-test-machine); } } context extension { includes { dialextens; dialthrus; } 5 => { Record(recording:gsm); Background(recording); } 81 => { iterations=1000000; Set(time1=${EPOCH}); for(i=1; ${i}<${iterations}; i=${i}+1) { NoOp(Hello); } Set(time2=${EPOCH}); Verbose(The time diff is $[${time2} - ${time1} ] seconds); Verbose(Which means that the priorities/sec = $[4* ${iterations} / (${time2} - ${time1}) ]); SayNumber($[4 * ${iterations} / (${time2} - ${time1}) ]); } 82 => { &ndeep(100000); Verbose(Finished 100000 levels deep call!); } 83 => { switch (${EXTEN}) { pattern 8X: Verbose(do something to prepare it); pattern 9X: Verbose(handle both 8x and 9x calls); pattern [4-7]X: Verbose(and this too!); } } } macro ndeep(level) { if( ${level} == 0) { Verbose(2|Got to Level 0); return; } &ndeep($[${level}-1]); return; } asterisk-1.4.21.2/pbx/ael/ael-test/setref0000755000175000017500000000015310423206707017314 0ustar maniacmaniac#!/bin/bash for i in res.*; do refname=`echo $i | sed 's/^res/ref/'` echo $refname mv $i $refname done asterisk-1.4.21.2/pbx/ael/ael-test/ref.ael-test190000644000175000017500000000323310710413372020463 0ustar maniacmaniac (If you find progress and other non-error messages irritating, you can use -q to suppress them) (You can use the -w option to dump extensions.conf format to extensions.conf.aeldump) LOG: lev:2 file:pbx_ael.c line:4090 func: pbx_load_module Starting AEL load process. LOG: lev:2 file:pbx_ael.c line:4097 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:4105 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. LOG: lev:4 file:pbx_ael.c line:2250 func: check_context_names Error: file ./extensions.ael, line 49-62: The context name (incoming) is also declared in file ./extensions.ael, line 62-69! (and neither is marked 'extend') LOG: lev:3 file:pbx_ael.c line:2234 func: check_switch_expr Warning: file ./extensions.ael, line 245-246: A default case was automatically added to the switch. LOG: lev:3 file:pbx_ael.c line:2347 func: check_pval_item Warning: file ./extensions.ael, line 312-312: macro call to non-existent funcA ! Hopefully it is present in extensions.conf! LOG: lev:3 file:pbx_ael.c line:2347 func: check_pval_item Warning: file ./extensions.ael, line 313-313: macro call to non-existent funcD ! Hopefully it is present in extensions.conf! LOG: lev:3 file:pbx_ael.c line:1287 func: check_goto Warning: file ./extensions.ael, line 319-319: goto: no context test5 could be found that matches the goto target! LOG: lev:4 file:pbx_ael.c line:4118 func: pbx_load_module Sorry, but 0 syntax errors and 1 semantic errors were detected. It doesn't make sense to compile. LOG: lev:4 file:ael2_parse line:543 func: main 0 contexts, 0 extensions, 0 priorities asterisk-1.4.21.2/pbx/ael/ael-test/ael-vtest13/0000755000175000017500000000000011041455740020147 5ustar maniacmaniacasterisk-1.4.21.2/pbx/ael/ael-test/ael-vtest13/telemarket_torture.ael20000755000175000017500000006660410466233565024665 0ustar maniacmaniac// // AN EXCERSIZE IN BAD DIALPLAN DESIGN // (What better testing ground than on telemarketers?) // // BAD DESIGN: long, boring introductions followed by long, drawn out menus of choices. // if they survive to the last option, how will they remember the choices? // // BAD DESIGN: Amateur Recording. Poor voice quality, too quiet. // Also, the announcer is definitely not vocally gifted. // Also, the long pauses and clicks between the intro // and menu choices might lead some to think that // the announcements are over, and hang up. Too bad! // WORSE DESIGN: Instead of using the Background application, the Playback // application is used. After taking so much time and trouble // to record this material, the caller must listen and enjoy // every syllable before they can make an option choice. None // of that interrupting with a choice. We want them to savour // every word! // GOOD/BAD, ER INSIDIOUS -- DANGLE A CARROT-- GIVE THE LISTENER A GOOD REASON TO // HANG ON AND VOLUNTARILY LISTEN TO THE TORTURE. // BUT, DON'T MAKE PROMISES YOU WON'T KEEP! context telemarket { s => { begin: Playback(telemarketer-intro); // ; Script: // Due to the extremely high volume of calls from everything from telemarketers // to Septic System Bacteria vendors, we are asking all such organizations // to remove this number from their call list, or as need be, to add this // number to their No-Call list, whichever is relevent. // [THE CARROT:] // We HAVE made some exceptions, and if you wish to see if your organization // has been exempted, please listen to and follow the following prompts. // // Otherwise, please Cease calling this number! // Playback(telemarketer-choices); // if you represent a charitable organization, please dial 1, // if you represent a political organization, please dial 2. // if you represent a polling company, please dial 3, // if you represent a market research organization, please dial 4. // if you represent a magazine or newsletter, please dial 5. // if you represent a commercial organization, please dial 6. } 1 => goto telemarket-charity|s|begin; 2 => goto telemarket-political|s|begin; 3 => goto telemarket-pollster|s|begin; 4 => goto telemarket-research|s|begin; 5 => goto telemarket-magazine|s|begin; 6 => goto telemarket-commercial|s|begin; 7 => goto telemarket-other|s|begin; t => goto telemarket|s|begin; i => goto telemarket|s|begin; o => goto telemarket|s|begin; } context telemarket-charity { s => { begin: Playback(telemark-charity-intro); // We have contributed generously to many worthy causes in the past, and will // continue to do so in the future. But we suspect that such organizatons // have sold our name and phone number to each other until we are now hounded // day and night by literally hundreds of such organizations. // Enough is Enough! // // If we have contributed to your cause in the past, we may, perhaps, be disposed to // do so in the future, at our option, // we give no pledges nor make any commitments here. // Send us material via the post if you feel this necessary // but do not even consider email. Any email or further phone calls from your organization // in the future, will be considered an act of aggression, and we will // blacklist your organization for the rest of our natural lives. // // To see if your organization is exempt from these prohibitions, please // comply with the following options. Playback(telemark-charity-choices); // If your organization is disease or genetic defect related, dial 1, // If your organization is handicap related, dial 2. // If your organization is a police or fireman or other similar support entity, please dial 3. // If your organization is a grade school to high school related // fund raiser or other type of activity, please dial 4. // If your organization is a college or univerity or alumnis organization, please dial 5. // If your organization is animal rights or ecology related organization, please dial 6. // If your organization is a political action or candidate support related, please dial 7. // If your organization is a substance abuse related organization or cause, please dial 8. // And any other charity or tax exempt organization should dial 9. } 1 => goto telemarket-char-disease|s|begin; 2 => goto telemarket-char-handicap|s|begin; 3 => goto telemarket-char-police|s|begin; 4 => goto telemarket-char-school|s|begin; 5 => goto telemarket-char-college|s|begin; 6 => goto telemarket-char-animal|s|begin; 7 => goto telemarket-char-candidate|s|begin; 8 => goto telemarket-char-abuse|s|begin; 9 => goto telemarket-char-other|s|begin; // BAD DESIGN: referring all timeouts,invalid choices, etc, back to the root of the menu tree will frustrate users no end! // WORSE DESIGN: How about having the user have to push a button to repeat the current menu? When a time out could just // automatically do it for the user? t => goto telemarket|s|begin; i => goto telemarket|s|begin; o => goto telemarket|s|begin; } context telemarket-char-disease { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-char-handicap { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-char-police { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-char-school { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-char-college { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-char-animal { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-char-candidate { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-char-abuse { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-char-other { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-sorry { s => { begin: Playback(telemarket-sorry); // Sorry -- your organization is not exempt. Please stop calling us. // Thank you. goodbye. Hangup(); } } // BAD DESIGN: Hanging up on your audience, no matter what the outcome, is not a nice thing to do! context telemarket-exception { s => { begin: Playback(telemarket-success); // Congratulations. Your organization IS exempt. Please call us back, // but this time, just act like a normal caller. Thank you. Goodbye. Hangup(); } } // BAD DESIGN: Making long cascading menu choices is a nasty thing to do to callers! // BAD DESIGN: Putting the most frequently encountered items at the end of a list is also a nasty thing to do! // GOOD DESIGN: All rejection notices use a single context. All Acceptance also. To change a rejection to an // acceptance, just change the reference from telemarket-sorry to telemarket-exception context telemarket-political { s => { begin: Playback(telemark-polit-intro); // To see if your organization is exempt from our prohibitions, // please follow the following prompts. // please note that they are not in alphabetical order, and you will have to // give them your full attention. Playback(telemark-polit-choices); // if You represent the America First Party, dial 1. // if You represent the American Party, dial 2. // if You represent the American Heritage Party, dial 3. // if You represent the American Independent Party, dial 4. // if You represent the American Nazi Party, dial 5. // if You represent the Pot Party, dial 6. // if You represent the American Reform Party, dial 7. // if You represent the Christian Falenqist Party of America, dial 8. // all others, please dial 9. } 1 => goto telemarket-poli-Am1st|s|begin; 2 => goto telemarket-poli-American|s|begin; 3 => goto telemarket-poli-AmHer|s|begin; 4 => goto telemarket-poli-AmInd|s|begin; 5 => goto telemarket-poli-AmNaz|s|begin; 6 => goto telemarket-poli-Pot|s|begin; 7 => goto telemarket-poli-AmRef|s|begin; 8 => goto telemarket-poli-CFP|s|begin; 9 => goto telemarket-political2|s|begin; t => goto telemarket|s|begin; i => goto telemarket|s|begin; o => goto telemarket|s|begin; } context telemarket-political2 { s => { begin: Playback(telemark-politx-intro); // Thank you for your patience, and I congratulate you for your persistence. // Just a few more options! // Playback(telemark-polit2-choices); // if You represent the Communist Party USA, dial 1. // if You represent the Constitution Party, dial 2. // if You represent the Family Values Party, dial 3. // if You represent the Freedom Socialist Party, dial 4. // if You represent the Grass Roots Party, dial 5. // if You represent the Green Party, dial 6. // if You represent the Greens Party, dial 7. // if You represent the Independence Party, dial 8. // all others, goto 9. } 1 => goto telemarket-poli-Communist|s|begin; 2 => goto telemarket-poli-Constit|s|begin; 3 => goto telemarket-poli-FamVal|s|begin; 4 => goto telemarket-poli-FreedSoc|s|begin; 5 => goto telemarket-poli-Grassroot|s|begin; 6 => goto telemarket-poli-Green|s|begin; 7 => goto telemarket-poli-Greens|s|begin; 8 => goto telemarket-poli-Independence|s|begin; 9 => goto telemarket-political3|s|begin; t => goto telemarket|s|begin; i => goto telemarket|s|begin; o => goto telemarket|s|begin; } context telemarket-political3 { s => { begin: Playback(telemark-politx-intro); Playback(telemark-polit3-choices); // if You represent the Independant American Party, dial 1. // if You represent the Labor Party, dial 2. // if You represent the Libertarian Party, dial 3. // if You represent the Light Party, dial 4. // if You represent the Natural Law Party, dial 5. // if You represent the New Party, dial 6. // if You represent the New Union Party, dial 7. // if You represent the Peace and Freedom Party, dial 8. // all others, hang on, dial 9. } 1 => goto telemarket-poli-IndAm|s|begin; 2 => goto telemarket-poli-Labor|s|begin; 3 => goto telemarket-poli-Liber|s|begin; 4 => goto telemarket-poli-Light|s|begin; 5 => goto telemarket-poli-NatLaw|s|begin; 6 => goto telemarket-poli-New|s|begin; 7 => goto telemarket-poli-NewUn|s|begin; 8 => goto telemarket-poli-PeaceFree|s|begin; 9 => goto telemarket-political4|s|begin; t => goto telemarket|s|begin; i => goto telemarket|s|begin; o => goto telemarket|s|begin; } context telemarket-political4 { s => { begin: Playback(telemark-politx-intro); Playback(telemark-polit4-choices); // if You represent the Prohibition Party, dial 1. // if You represent the Reform Party, dial 2. // if You represent the Revolution , dial 3. // if You represent the Socialist Party USA, dial 4. // if You represent the Socialist Action Party, dial 5. // if You represent the Socialist Equality Party, dial 6. // if You represent the Socialist Labor Party, dial 7. // if You represent the Socialist Workers Party, dial 8. // all others, hang on, and dial 9. } 1 => goto telemarket-poli-Prohib|s|begin; 2 => goto telemarket-poli-Ref|s|begin; 3 => goto telemarket-poli-Revol|s|begin; 4 => goto telemarket-poli-SocPart|s|begin; 5 => goto telemarket-poli-SocAct|s|begin; 6 => goto telemarket-poli-SocEq|s|begin; 7 => goto telemarket-poli-SocLab|s|begin; 8 => goto telemarket-poli-SocWork|s|begin; 9 => goto telemarket-political5|s|begin; t => goto telemarket|s|begin; i => goto telemarket|s|begin; o => goto telemarket|s|begin; } context telemarket-political5 { s => { begin: Playback(telemark-politx-intro); Playback(telemark-polit5-choices); // if You represent the Southern Party, dial 1. // if You represent the Southern Independence Party, dial 2. // if You represent the US Pacifist Party, dial 3. // if You represent the We the People Party, dial 4. // if You represent the Workers World Party, dial 5. // if You represent the Democratic Party, dial 6. // if You represent the Republican Party, dial 7. // all others, may dial 8. } 1 => goto telemarket-poli-South|s|begin; 2 => goto telemarket-poli-SoInd|s|begin; 3 => goto telemarket-poli-USPac|s|begin; 4 => goto telemarket-poli-WTP|s|begin; 5 => goto telemarket-poli-WWP|s|begin; 6 => goto telemarket-poli-Democrat|s|begin; 7 => goto telemarket-poli-Repub|s|begin; 8 => goto telemarket-poli-other|s|begin; t => goto telemarket|s|begin; i => goto telemarket|s|begin; o => goto telemarket|s|begin; } context telemarket-poli-other { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-Repub { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-Democrat { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-WWP { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-WTP { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-USPac { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-SoInd { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-South { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-SocWork { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-SocLab { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-SocEq { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-SocAct { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-SocPart { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-Revol { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-Ref { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-Prohib { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-PeaceFree { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-NewUn { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-New { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-NatLaw { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-Light { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-Liber { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-Labor { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-IndAm { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-Independence { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-Greens { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-Green { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-Grassroot { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-FreedSoc { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-FamVal { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-Constit { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-Communist { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-CFP { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-AmRef { s => { begin: goto telemarket-sorry|s|begin; } } // BAD DESIGN: Putting in infinite loops in the menus, whether by design or mistake is not nice! context telemarket-poli-Pot { s => { begin: goto telemarket-political|s|begin; // will the Pot Party Guys even notice an infinite loop? } } context telemarket-poli-AmNaz { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-AmInd { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-AmHer { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-American { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-Am1st { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-pollster { s => { begin: Playback(telemark-poll-intro); // I'm sorry-- We are just not available for doing any polling at the moment. So, // please remove us from your list. goto telemarket-sorry|s|begin; } t => goto telemarket|s|begin; i => goto telemarket|s|begin; o => goto telemarket|s|begin; } context telemarket-research { s => { begin: Playback(telemark-research-intro); // I'd like to say I'd love to help you with your market survey, but that would be a complete // and total lie. I am not interested in helping you with Market Surveys. // // Please remove me from your call list. It just doesn't pay enough. But Thank you. goto telemarket-sorry|s|begin; } t => goto telemarket|s|begin; i => goto telemarket|s|begin; o => goto telemarket|s|begin; } context telemarket-magazine { s => { begin: Playback(telemark-mag-choices); // If you are calling to see if I would like a NEW free subscription // to your magazine or newsletter, please dial 1. // If you are calling to see if I want to Renew an existing subscription, please dial 2. // If you are representing some publisher, and want my opinion about something, or are doing // some kind of survey, please dial 3. // If you are calling to verify that some previous caller actually called me, and the // verification information is correct, please dial 4. // and if your call purpose doesn't match any of the above, please dial 5. } 1 => goto telemark-mag-new|s|begin; 2 => goto telemark-mag-renew|s|begin; 3 => goto telemark-mag-survey|s|begin; 4 => goto telemark-mag-verify|s|begin; 5 => goto telemark-mag-other|s|begin; t => goto telemarket|s|begin; i => goto telemarket|s|begin; o => goto telemarket|s|begin; } context telemark-mag-new { s => { begin: Playback(telemark-mag-new); // I'm sorry, I'm maxed out, and the answer is NO. // If you really think I'd LOVE to add your publication to the pile I already get, // Send something via the post. Don't call me. // Thank you. bye. Hangup(); } t => goto telemarket|s|begin; i => goto telemarket|s|begin; o => goto telemarket|s|begin; } context telemark-mag-renew { s => { begin: Playback(telemark-mag-renew); // So, you want to see if I want to Renew, do you? The answer is most likely "YES". // // But, I will not answer a long list of questions over the phone. Send such // categorization info via the post, and stop bothering me over the phone, // if this is what you want. // Do you need verification information? Normally I opt out of such nonsense, if possible. // If not, use whatever of the following you can: // My birth month is October. // My birthplace is Kigali, in Rwanda, in Afica. // My eye color is orange. // All of these are wonderfully false, but I use them regularly for such purposes. Thank you. Hangup(); } t => goto telemarket|s|begin; i => goto telemarket|s|begin; o => goto telemarket|s|begin; } context telemark-mag-survey { s => { begin: Playback(telemark-mag-survey); // Sorry, I don't have time to answer survey or opinion questions. Find someone // else to help build your marketing database, I guess. Good Luck. Hangup(); } t => goto telemarket|s|begin; i => goto telemarket|s|begin; o => goto telemarket|s|begin; } context telemark-mag-verify { s => { begin: Playback(telemark-mag-verify); // If you are calling to verify that your own agents aren't ripping you off, // sorry, I can't help you. I opt out whenever I can, mainly because I'm not // paid enough for this kind of thing. I always lie, and I can't remember // what I might have said. Sorry. Goodbye. Hangup(); } t => goto telemarket|s|begin; i => goto telemarket|s|begin; o => goto telemarket|s|begin; } context telemark-mag-other { s => { begin: goto telemarket-sorry|s|begin; } } // BAD DESIGN: Is it entrapment, when you lure telemarketers to reveal their contact information, // Just so you can report them to the FTC/FCC? If it is, isn't it unethical for them // to hide their CID (via Anonymous, usually), to hide their identities from the public? // BTW -- What telemarketer would be stupid enough to fall for this? I'll bet not a single one! // For that matter, what telemarketer will be stupid enough to even enter any of this? I'll bet not a single one! // (but it was fun messing around). context telemarket-commercial { s => { begin: Playback(telemark-comm-intro); // Script: Please leave your name, organization, and phone number, plus // a short description of the purpose of your call, at the prompt. // We will do our best to respond to your call! And, in the mean time, // do not forget to add us to your no-call list! Voicemail(u82); goto telemarket-sorry|s|begin; } t => goto telemarket|s|begin; i => goto telemarket|s|begin; o => goto telemarket|s|begin; } context telemarket-other { s => { begin: Playback(telemark-other-intro); // Please review the previous menu options, and see if you really don't // fit in one of the previous categories. // If you do not, go ahead, and call me again, and let me know what category // I should have included in the above list. I appreciate this. Thank you much! Hangup(); } t => goto telemarket|s|begin; i => goto telemarket|s|begin; o => goto telemarket|s|begin; } asterisk-1.4.21.2/pbx/ael/ael-test/ael-vtest13/include2.ael20000644000175000017500000000014210466233565022427 0ustar maniacmaniac NoOp(This was included from include2.ael2); #include "include3.ael2" #include "include4.ael2" asterisk-1.4.21.2/pbx/ael/ael-test/ael-vtest13/include1.ael20000644000175000017500000000011610466233565022427 0ustar maniacmaniac NoOp(Hello, this is included from include1.ael2); #include "include2.ael2" asterisk-1.4.21.2/pbx/ael/ael-test/ael-vtest13/include4.ael20000644000175000017500000000012710466233565022434 0ustar maniacmaniac NoOp(This is include4.ael2! Isn't it cool!?!?!?!); NoOp(4 doesn't include anything); asterisk-1.4.21.2/pbx/ael/ael-test/ael-vtest13/include3.ael20000644000175000017500000000007110466233565022431 0ustar maniacmaniac NoOp(This is include3.ael2!); #include "include5.ael2" asterisk-1.4.21.2/pbx/ael/ael-test/ael-vtest13/include5.ael20000644000175000017500000000007010466233565022432 0ustar maniacmaniac NoOp(Include5.ael2 doesn't include anything, either!); asterisk-1.4.21.2/pbx/ael/ael-test/ael-vtest13/extensions.ael0000755000175000017500000022241410466233565023052 0ustar maniacmaniacglobals { static=yes; writeprotect=yes; CONSOLE=Console/dsp; // Console interface for demo IAXINFO=murf:tlhfckoct; // IAXtel username/password FWDNUMBER=544788 ; // your calling number FWDCIDNAME="Joe-Worker"; // your caller id FWDPASSWORD=zingledoodle ; // your password FWDRINGS=Zap/6 ; // the phone to ring FWDVMBOX=1 ; // the VM box for this user } macro std-exten( ext , dev ) { Dial(${dev}/${ext},20); switch(${DIALSTATUS}) { case BUSY: Voicemail(b${ext}); break; case NOANSWER: Voicemail(u${ext}); break; case ANSWER: break; default: Voicemail(u${ext}); } catch a { VoiceMailMain(${ext}); } } macro std-priv-exten_1( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_2( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_3( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_4( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_5( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_6( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_7( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_8( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_9( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_10( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_11( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_12( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_13( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_14( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_15( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_16( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_17( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_18( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_19( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_20( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_21( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_22( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_23( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_24( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_25( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_26( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_27( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_28( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_29( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_30( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_31( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_32( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_33( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_34( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_35( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_36( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_37( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_38( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_39( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_40( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_41( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_42( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_43( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_44( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_45( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_46( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_47( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_48( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_49( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_50( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_51( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_52( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_53( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_54( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_55( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_56( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_57( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_58( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_59( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_60( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_61( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_62( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_63( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_64( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_65( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_66( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_67( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_68( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_69( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_70( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_71( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_72( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_73( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro fillcidname() { if( "${CALLERID(number)}" = "" ) // nothing to work with, quit!!! return; Set(cidn=${DB(cidname/${CALLERID(num)})}); if( "${CALLERID(name)}" != "" ) { if( ("${cidn}" = "Privacy Manager" & "${CALLERID(name)}" != "Privacy Manager") | "${cidn}" = "" ) // if the entry isn't in the database, // or if an entry exists, and it's "Privacy Manager", empty, (or add other useless possibilities). { Set(DB(cidname/${CALLERID(number)})=${CALLERID(name)}); // then set or override what's in the DB } } // Now, we fill in the callerid info from the incoming entry, if it's stuff worth using // Ignore fundamentally semi-anonymous information from local cell phones // if the db has an entry for this number, and it's not a canned string from a cell phone company if( ( "${cidn}" != "" ) & ( "${CALLERID(name)}" = "" | "${CALLERID(name)}" = "CODY,WY " | "${CALLERID(name)}" = "POWELL,WY " | "${CALLERID(name)}" = "WIRELESS CALLER" | "${CALLERID(name)}" = "SUBSCRIBER,WIRE" | "${CALLERID(name)}" = "CELLULAR ONE" | "${CALLERID(name)}" = "Cellular One Customer" | "${CALLERID(name)}" = "CELLULAR ONE " | "${CALLERID(name)}" = "Privacy Manager" | "${CALLERID(name)}" = "RIVERTON,WY " | "${CALLERID(name)}" = "BASIN,WY " | "${CALLERID(name)}" = "BILLINGS,MT " | "${CALLERID(name)}" = "PROVO,UT " | "${CALLERID(name)}" = "TOLL FREE " ) ) // put stuff in the above, that the phone company tends to put in your callerid, // that you would rather override with DB info // there's no way to guess them all, but you can get the most popular ones... // why cell phones can't do CID like everybody else, ....? { Set(CALLERID(name)=${cidn}); // Override what the phone company provides with what's in the DB for this number. } } macro ciddial(dialnum, lookup, waittime, dialopts, ddev) { Set(cidnu=${CALLERID(num)}); Set(cidn=${DB(cidname/${lookup})}); Set(CALLERID(name)=${cidn}); Dial(${ddev}/${dialnum}|${waittime}|${dialopts}); if( "${DIALSTATUS}" = "CHANUNAVAIL" ) { BackGround(try_voip); CALLERID(num)=7075679201; Dial(SIP/1${lookup}@tctwest,${waittime},${dialopts}); if( "${DIALSTATUS}" = "CHANUNAVAIL" ) { BackGround(try_cell); CALLERID(num)=${cidnu}; // put the original number back Dial(Zap/2/${lookup},${waittime},${dialopts}); } } } macro ciddial3(dialnum, lookup, waittime, dialopts, ddev) { Set(cidnu=${CALLERID(num)}); Set(cidn=${DB(cidname/${lookup})}); Set(CALLERID(name)=${cidn}); Dial(${ddev}/${dialnum}|${waittime}|${dialopts}); if( "${DIALSTATUS}" = "CHANUNAVAIL" ) { BackGround(try_cell); Dial(Zap/2/${lookup},${waittime},${dialopts}); } } macro ciddial2(dialnum, lookup, waittime, dialopts, ddev) // give priority to tctwest, then the ZAP in emergencies { Set(cidn=${DB(cidname/${lookup})}); Set(cidnu=${CALLERID(num)}); Set(CALLERID(name)=${cidn}); Set(CALLERID(num)=7075679201); Dial(SIP/1${lookup}@tctwest,${waittime},${dialopts}); if( "${DIALSTATUS}" = "CHANUNAVAIL" ) { Set(CALLERID(num)=${cidnu}); // put the original number back BackGround(try_zap); Dial(${ddev}/${dialnum},${waittime}|${dialopts}); if( "${DIALSTATUS}" = "CHANUNAVAIL" ) { BackGround(try_cell); Dial(Zap/2/${lookup},${waittime},${dialopts}); } } } macro callerid-liar() { TrySystem(/usr/bin/play /var/lib/asterisk/sounds/priv-callerintros/LIAR.gsm&); Background(priv-liar); // Script: OOOps! Sorry! I don't allow men with ski masks pulled over their // faces to get in the front door, and unidentified callers won't fair // any better. You entered *MY* phone number. That won't work. // If you are telemarketing, cross me off the list, and don't call again. // If you did this by mistake, forgive my defenses, and call again. // Alternate: (priv-liar2) // Script: You have chosen to try to deceive my system and withold your CallerID, // by entering my own phone number as YOUR CallerID. I find this // offensive because you are being dishonest. I will not do business nor // waste my time talking to anyone who is less than honest and forthcoming. // Take me off your call list and do not call me again. Hangup(); } macro callerid-bad() { mycid=${CALLERID(num)}:"1([0-9]+)"; Set(CALLERID(num)=${mycid}); Wait(0); } context privacyManagerFailed { s => { begin: Background(PrivManInstructions); // Script: OOps, that didn't go well. You need to enter *your* area code, and *your* 7 digit // phone number, for a total of 10 digits, or you'll be handed over to the monkeys. Let's // try this again, and hopefully you can get past our front-line defenses! PrivacyManager(); if( "${PRIVACYMGRSTATUS}" = "FAILED" ) { Background(tt-allbusy); Background(tt-somethingwrong); Background(tt-monkeysintro); Background(tt-monkeys); Background(tt-weasels); Hangup(); } else { goto homeline|s|postPriv; } } } // Some comments // Some more comments context homeline { s => { begin: Answer(); Set(repeatcount=0); Zapateller(nocallerid); PrivacyManager(); if( "${PRIVACYMGRSTATUS}" = "FAILED" ) { TrySystem(/usr/bin/play /var/lib/asterisk/sounds/privmanfailed.gsm); &std-priv-exten(Zap/3r1&Zap/5r1,2,25,mtw,telemarket,telemarket); Hangup(); return; // goto privacyManagerFailed|s|begin; } postPriv: &fillcidname(); Set(CONFCIDNA=${CALLERID(name)}); Set(CONFCIDNU=${CALLERID(num)}); AGI(callall); AGI(submit-announce.agi); if( "${CALLERID(num)}" : "1" ) { &callerid-bad(); } if( "${CALLERID(num)}" = "7077577685" & "${CALLERID(name)}" : "Privacy Manager" ) { &callerid-liar(); } TrySystem(/usr/local/bin/who-is-it ${CALLERID(num)} "${CALLERID(name)}"&); Set(lds=${DB(playlds/${CALLERID(num)})}); if( "${lds}" = "1" ) { SetMusicOnHold(mohlds); } direct=${DB(DirectCall/${CALLERID(num)})}; if( "${direct}" != "" & ${direct} != 0 ) { verbose(direct is XXX#${direct}XXXX); Playback(greetings/direct); // Welcome to the Murphy residence. This system will automatically try to connect you to... Playback(/var/spool/asterisk/voicemail/default/${direct}/greet); TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm); TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/${direct}/greet.wav&); switch(${direct}) { case 1: //Steve &std-priv-exten(Zap/6r3&Sip/murf,1,25,mpA(beep)tw,telemarket,telemarket); goto s|loopback; case 2: //Sonya &std-priv-exten(Zap/3r1&Zap/5r1,2,25,mtw,telemarket,telemarket); goto s|loopback; default: // all the kids Set(z=${direct}-2); goto homeline-kids|${z}|1; } } loopback: ifTime(*|*|20-25|dec) { Playback(greetings/christmas); } else ifTime(*|*|31|dec) { Playback(greetings/newyear); } else ifTime(*|*|1|jan) { Playback(greetings/newyear); } else ifTime(*|*|14|feb) { Playback(greetings/valentines); } else ifTime(*|*|17|mar) { Playback(greetings/stPat); } else ifTime(*|*|31|oct) { Playback(greetings/halloween); } else ifTime(*|mon|15-21|jan) { Playback(greetings/mlkDay); } else ifTime(*|thu|22-28|nov) { Playback(greetings/thanksgiving); } else ifTime(*|mon|25-31|may) { Playback(greetings/memorial); } else ifTime(*|mon|1-7|sep) { Playback(greetings/labor); } else ifTime(*|mon|15-21|feb) { Playback(greetings/president); } else ifTime(*|sun|8-14|may) { Playback(greetings/mothers); } else ifTime(*|sun|15-21|jun) { Playback(greetings/fathers); } else { Playback(greetings/hello); // None of the above? Just a plain hello will do } Background(murphy-homeline-intro1); // Script: Hello-- Welcome to the Murphy's! If you already know what // option you want, you don't have to wait for this entire spiel-- just // have at it. // If you are calling because this number is on a list of some sort, dial 6. // If you want Sonya, dial 1. // If you want one of the kids, dial 2. // If you want Steve, dial 3. // to play with your introduction, dial 5. // If we don't seem to be giving you the time of day, try 7. // Have a good day! } 1 => { // Sonya TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm); TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/2/greet.wav&); &std-priv-exten(Zap/3r1&Zap/5r1,2,25,mtw,telemarket,telemarket); goto s|loopback; } 2 => { // Kids goto homeline-kids|s|begin; } 21 => { Dial(IAX2/seaniax,20,T); } 3 => { // Steve &std-priv-exten(Zap/6r3&Sip/murf,1,25,mpA(beep)tw,telemarket,telemarket); goto s|loopback; } 4 => { // Voicemail VoicemailMain(); goto s|loopback; } 5 => { // play with intro goto home-introduction|s|begin; } 6 => { // Telemarketers goto telemarket|s|begin; } 7 => { // time of day, riddle agi(tts-riddle.agi); Background(gsm/what-time-it-is2); SayUnixTime(); goto s|loopback; } 792 => { // Page All goto pageall|s|begin; } 793 => { // check the tone recognition Read(zz,,0,,1,0); SayDigits(${zz}); } t => { Set(repeatcount=${repeatcount} + 1); if( ${repeatcount} < 3 ) { goto s|loopback; // just loopback isn't enough } Hangup(); } i => { Background(invalid); goto s|loopback; } o => { Congestion(); } fax => { Dial(Zap/4); } } // Some comments // Some more comments context pageall { s => { begin: AGI(callall); MeetMe(5555,dtqp); MeetMeAdmin(5555,K); Hangup(); } h => { begin: MeetMeAdmin(5555,K); Background(conf-muted); Hangup(); } } // Some comments // Some more comments context add-to-conference { start => { NoCDR(); MeetMe(5555,dmqp); } h => { Hangup(); } } context home-introduction { s => { begin: Background(intro-options); // Script: To hear your Introduction, dial 1. // to record a new introduction, dial 2. // to return to the main menu, dial 3. // to hear what this is all about, dial 4. } 1 => { Playback(priv-callerintros/${CALLERID(num)}); goto s|begin; } 2 => { goto home-introduction-record|s|begin; } 3 => { goto homeline|s|loopback; } 4 => { Playback(intro-intro); // Script: // This may seem a little strange, but it really is a neat // thing, both for you and for us. I've taped a short introduction // for many of the folks who normally call us. Using the Caller ID // from each incoming call, the system plays the introduction // for that phone number over a speaker, just as the call comes in. // This helps the folks // here in the house more quickly determine who is calling. // and gets the right ones to gravitate to the phone. // You can listen to, and record a new intro for your phone number // using this menu. goto s|begin; } t => { goto s|begin; } i => { Background(invalid); goto s|begin; } o => { goto s|begin; } } context home-introduction-record { s => { begin: Background(intro-record-choices); // Script: // If you want some advice about recording your // introduction, dial 1. // otherwise, dial 2, and introduce yourself after // the beep. } 1 => { Playback(intro-record); // Your introduction should be short and sweet and crisp. // Your introduction will be limited to 10 seconds. // This is NOT meant to be a voice mail message, so // please, don't say anything about why you are calling. // After we are done making the recording, your introduction // will be saved for playback. // If you are the only person that would call from this number, // please state your name. Otherwise, state your business // or residence name instead. For instance, if you are // friend of the family, say, Olie McPherson, and both // you and your kids might call here a lot, you might // say: "This is the distinguished Olie McPherson Residence!" // If you are the only person calling, you might say this: // "This is the illustrious Kermit McFrog! Pick up the Phone, someone!!" // If you are calling from a business, you might pronounce a more sedate introduction,like, // "Fritz from McDonalds calling.", or perhaps the more original introduction: // "John, from the Park County Morgue. You stab 'em, we slab 'em!". // Just one caution: the kids will hear what you record every time // you call. So watch your language! // I will begin recording after the tone. // When you are done, hit the # key. Gather your thoughts and get // ready. Remember, the # key will end the recording, and play back // your intro. Good Luck, and Thank you!" goto 2|begin; } 2 => { begin: Background(intro-start); // OK, here we go! After the beep, please give your introduction. Background(beep); Record(priv-callerintros/${CALLERID(num)}:gsm,3); Background(priv-callerintros/${CALLERID(num)}); goto home-introduction|s|begin; } t => { goto s|begin; } i => { Background(invalid); goto s|begin; } o => { goto s|begin; } } context homeline-kids { s => { begin: Background(murphy-homeline-kids); // Which Kid? 1=Sean, 2:Eric, 3:Ryan, 4:Kyle, 5:Amber, 6:Alex, 7:Neal } 1 => { // SEAN TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm); TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/3/greet.wav&); // &std-priv-exten(Zap/3r2&Zap/5r2,3,35,mtw,telemarket,telemarket); &std-priv-exten(IAX2/seaniax&Zap/5r2,3,35,mtw,telemarket,telemarket); goto homeline|s|loopback; } 2 => { // ERIC TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm); TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/4/greet.wav&); Voicemail(u4); goto homeline|s|loopback; // SetMusicOnHold(erics); // TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm); // TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/4/greet.wav&); // &std-priv-exten(Zap/3r2&Zap/5r2,4,35,mtw,telemarket,telemarket); // goto homeline|s|loopback; } 3 => { // RYAN TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm); TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/5/greet.wav&); &std-priv-exten(Zap/3r2&Zap/5r2,5,35,mtw,telemarket,telemarket); goto homeline|s|loopback; } 4 => { // KYLE TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm); TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/6/greet.wav&); &std-priv-exten(Zap/3r2&Zap/5r2,6,35,mtw,telemarket,telemarket); goto homeline|s|loopback; } 5 => { TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm); TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/7/greet.wav&); &std-priv-exten(Zap/3r2&Zap/5r2,7,35,mtw,telemarket,telemarket); goto homeline|s|loopback; } 6 => { TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm); TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/8/greet.wav&); &std-priv-exten(Zap/3r2&Zap/5r2,8,35,mtw,telemarket,telemarket); goto homeline|s|loopback; } 7 => { TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm); TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/9/greet.wav&); &std-priv-exten(Zap/3r2&Zap/5r2,9,35,mtw,telemarket,telemarket); goto homeline|s|loopback; } t => { goto s|begin; } i => { Background(invalid); goto s|begin; } o => { goto s|begin; } } context voipworkline { s => { begin: Answer(); TrySystem(/usr/local/bin/who-is-it ${CALLERID(num)} "${CALLERID(name)}"&); goto workline|s|loopback; } 7075679201 => { Answer(); TrySystem(/usr/local/bin/who-is-it ${CALLERID(num)} "${CALLERID(name)}"&); goto workline|s|loopback; } } context workline { s => { begin: Answer(); Wait(1); Set(repeatcount=0); Zapateller(nocallerid); // PrivacyManager(); // if( "${PRIVACYMGRSTATUS}" = "FAILED" ) // { // goto privacyManagerFailed|s|begin; // } &fillcidname(); TrySystem(/usr/local/bin/who-is-it ${CALLERID(num)} "${CALLERID(name)}"&); loopback: Background(greetings/greeting); //script: Hello Background(murphy-office-intro1); //script: welcome to Steve Murphy's office. If you are dialing // this number because it was on a calling list of any sort, dial 6. // Otherwise, dial 1, and hopefully, you will reach Steve. } 1 => { TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm); TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/1/greet.wav&); &std-priv-exten(Zap/6&Sip/murf,1,30,mtw,telemarket,telemarket); goto s|loopback; } 4 => { VoicemailMain(); goto s|loopback; } 6 => { goto telemarket|s|begin; } 793 => { // check the tone recognition Read(zz,,0,,1,0); SayDigits(${zz}); } t => { repeatcount=${repeatcount} + 1; if( ${repeatcount} < 3 ) { goto s|loopback; // just loopback isn't enough } Hangup(); } i => { Background(invalid); goto s|loopback; } o => { Congestion(); } fax => { Answer(); Dial(Zap/4); } } context dialFWD { ignorepat => 8; ignorepat => 9; _83. => { Set(CALLERID(name)=${FWDCIDNAME}); Dial(IAX2/${FWDNUMBER}:${FWDPASSWORD}@iax2.fwdnet.net/${EXTEN:2},60,r); Congestion(); } _82NXX => { Set(CALLERID(name)=${FWDCIDNAME}); Dial(IAX2/${FWDNUMBER}:${FWDPASSWORD}@iax2.fwdnet.net/${EXTEN:2},60,r); Congestion(); } _92NXX => { Set(CALLERID(name)=${FWDCIDNAME}); Dial(IAX2/${FWDNUMBER}:${FWDPASSWORD}@iax2.fwdnet.net/${EXTEN:2},60,r); Congestion(); } } context dialiaxtel { ignorepat => 8; ignorepat => 9; _81700NXXXXXX => { Dial(IAX2/zorch:zilchnoodle@iaxtel.com/${EXTEN:1}@iaxtel); } _81800NXXXXXX => { Dial(IAX2/zorch:zilchnoodle@iaxtel.com/${EXTEN:1}@iaxtel); } _91700NXXXXXX => { Dial(IAX2/zorch:zilchnoodle@iaxtel.com/${EXTEN:1}@iaxtel); } _91800NXXXXXX => { Dial(IAX2/zorch:zilchnoodle@iaxtel.com/${EXTEN:1}@iaxtel); } } context dialgoiax { ignorepat => 9; _93. => { Set(CALLERID(name)="Joe Worker"); Dial(IAX2/878201007658:stickyfinger295@server1.goiax.com/${EXTEN:2},60,r); Congestion(); } } context homefirst { ignorepat => 9; _91NXXNXXXXXX => { &ciddial(${EXTEN:1},${EXTEN:2},30,TW,Zap/1); } _9754XXXX => { &ciddial(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1); } _9574XXXX => { &ciddial(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1); } _9202XXXX => { &ciddial(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1); } _9219XXXX => { &ciddial(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1); } _9254XXXX => { &ciddial(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1); } _9716XXXX => { &ciddial(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1); } _9NXXXXXX => { &ciddial(1707${EXTEN:1},707${EXTEN:1},30,TW,Zap/1); } _9011. => { &ciddial(${EXTEN:1},${EXTEN:1},30,TW,Zap/1); } _9911 => { Dial(Zap/1/911,30,T); } _9411 => { Dial(Zap/1/411,30,T); } } context workfirst { ignorepat => 9; _91NXXNXXXXXX => { &ciddial2(${EXTEN:1},${EXTEN:2},30,TW,Zap/1); } _9754XXXX => { &ciddial2(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1); } _9574XXXX => { &ciddial2(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1); } _9202XXXX => { &ciddial2(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1); } _9219XXXX => { &ciddial2(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1); } _9254XXXX => { &ciddial2(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1); } _9716XXXX => { &ciddial2(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1); } _9NXXXXXX => { &ciddial2(1707${EXTEN:1},707${EXTEN:1},30,TW,Zap/1); } _9911 => { Dial(Zap/1/911,30,T); } _9411 => { Dial(Zap/1/411,30,T); } } context force_cell { ignorepat => 8; _81NXXNXXXXXX => { &ciddial(${EXTEN:1}#,${EXTEN:2},30,TW,Zap/2); } _8754XXXX => { &ciddial(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/2); } _8574XXXX => { &ciddial(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/2); } _8202XXXX => { &ciddial(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/2); } _8219XXXX => { &ciddial(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/2); } _8254XXXX => { &ciddial(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/2); } _8716XXXX => { &ciddial(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/2); } _8NXXXXXX => { &ciddial(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/2); } _8911 => { Dial(Zap/1/911|30|T); } _8411 => { Dial(Zap/1/411|30|T); } } context force_home { ignorepat => 8; _81NXXNXXXXXX => { &ciddial3(${EXTEN:1}#,${EXTEN:2},30,TW,Zap/1); } _8754XXXX => { &ciddial3(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/1); } _8574XXXX => { &ciddial3(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/1); } _8202XXXX => { &ciddial3(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/1); } _8219XXXX => { &ciddial3(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/1); } _8254XXXX => { &ciddial3(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/1); } _8716XXXX => { &ciddial3(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/1); } _8NXXXXXX => { &ciddial3(1707${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/1); } _8911 => { Dial(Zap/1/911|30|T); } _8411 => { Dial(Zap/1/411|30|T); } } context homeext { ignorepat => 8; ignorepat => 9; includes { parkedcalls; homefirst; force_cell; } s => { loopback: Wait(0); } 1 => { &std-priv-exten(Zap/3&Zap/5,2,35,mtw,telemarket,telemarket); goto s|loopback; } 2 => { &std-priv-exten(Zap/6&Zap/5,1,35,mpA(beep3)Tt,telemarket,telemarket); goto s|loopback; } 4 => { VoicemailMain(); } 5 => { Record(recording:gsm); Background(recording); } 6 => { Background(recording); } 760 => { DateTime(); goto s|loopback; } 761 => { Record(announcement:gsm); TrySystem(/usr/bin/play /var/lib/asterisk/sounds/announcement.gsm&); goto s|loopback; } 762 => { agi(tts-riddle.agi); Background(gsm/what-time-it-is2); SayUnixTime(); goto s|loopback; } 763 => { Set(CALLERID(num)=); Dial(Zap/6r3,35,mptA(beep3)); //results: it should ALWAYS ask for an intro; the intro should not be left behind Hangup(); } 764 => { Set(CALLERID(num)=); Dial(Zap/6r3,35,mptnA(beep3)); //results: Don't save the intro; shouldn't anyway if no callerid Hangup(); } 765 => { Set(CALLERID(num)=); Dial(Zap/6r3,35,mptNA(beep3)); //results: Don't screen if there's CALLERID; it should screen the call. Hangup(); } 766 => { Dial(Zap/6r3,35,mptNA(beep3)); //results: Don't screen if there's CALLERID; it should screen the call. Hangup(); } 767 => { Dial(Zap/6r3,35,mptnA(beep3)); //results: Don't save the intro; the interesting case, because callerID should be present. Hangup(); } 769 => { Playtones(dial); Wait(2); Playtones(busy); Wait(2); Playtones(ring); Wait(2); Playtones(congestion); Wait(2); Playtones(callwaiting); Wait(2); Playtones(dialrecall); Wait(2); Playtones(record); Wait(2); Playtones(info); Wait(5); Hangup(); } 790 => { MeetMe(790,p); } 792 => { goto pageall|s|begin; } 795 => { AGI(wakeup.agi);Congestion(); } 544716 => { // Incoming call from FWD TrySystem(/usr/local/bin/who-is-it ${CALLERID(num)} "${CALLERID(name)}"&); goto s|loopback; } i => { Background(invalid); goto s|loopback; } o => { goto s|loopback; } t => { Congestion(); } } context fromvmhome { 1 => { Dial(Zap/6&Sip/murf|20|Tt); } 2 => { Dial(Zap/3&Zap/5|20|Tt); } _707202XXXX => { &ciddial(1${EXTEN:3},${EXTEN},30,TW,Zap/1); } _707219XXXX => { &ciddial(1${EXTEN:3},${EXTEN},30,TW,Zap/1); } _707254XXXX => { &ciddial(1${EXTEN:3},${EXTEN},30,TW,Zap/1); } _707716XXXX => { &ciddial(1${EXTEN:3},${EXTEN},30,TW,Zap/1); } _707754XXXX => { &ciddial(${EXTEN:3},${EXTEN},30,TW,Zap/1); } _707574XXXX => { &ciddial(${EXTEN:3},${EXTEN},30,TW,Zap/1); } _NXXNXXXXXX => { &ciddial(1${EXTEN},${EXTEN},30,TW,Zap/1); } _1NXXNXXXXXX => { // HAND DIALING &ciddial(${EXTEN},${EXTEN:1},30,TW,Zap/1); } _754XXXX => { &ciddial(${EXTEN},707${EXTEN},30,TW,Zap/1); } _574XXXX => { &ciddial(${EXTEN},707${EXTEN},30,TW,Zap/1); } _NXXXXXX => { &ciddial(1707${EXTEN},707${EXTEN},30,TW,Zap/1); } _911 => { &ciddial(911,911,30,TW,Zap/1); } _411 => { &ciddial(411,411,30,TW,Zap/1); } } context fromvmwork { 1 => { Dial(Zap/6&Sip/murf|20|Tt); } 2 => { Dial(Zap/3&Zap/5|20|Tt); } _707202XXXX => { &ciddial(1${EXTEN:3},${EXTEN},30,TW,Zap/1); } _707219XXXX => { &ciddial(1${EXTEN:3},${EXTEN},30,TW,Zap/1); } _707254XXXX => { &ciddial(1${EXTEN:3},${EXTEN},30,TW,Zap/1); } _707716XXXX => { &ciddial(1${EXTEN:3},${EXTEN},30,TW,Zap/1); } _707754XXXX => { &ciddial(${EXTEN:3},${EXTEN},30,TW,Zap/1); } _707574XXXX => { &ciddial(${EXTEN:3},${EXTEN},30,TW,Zap/1); } _NXXNXXXXXX => { &ciddial(1${EXTEN},${EXTEN},30,TW,Zap/1); } _1NXXNXXXXXX => { // HAND DIALING &ciddial(${EXTEN},${EXTEN:1},30,TW,Zap/1); } _754XXXX => { &ciddial(${EXTEN},707${EXTEN},30,TW,Zap/1); } _574XXXX => { &ciddial(${EXTEN},707${EXTEN},30,TW,Zap/1); } _NXXXXXX => { &ciddial(1707${EXTEN},707${EXTEN},30,TW,Zap/1); } 911 => { &ciddial(911,911,30,TW,Zap/1); } 411 => { &ciddial(411,411,30,TW,Zap/1); } } context fromSeanUniden { includes { parkedcalls; } 21 => { Dial(IAX2/seaniax,20,T); } _707202XXXX => { &ciddial(${EXTEN:3},${EXTEN},30,TW,Zap/1); } _707219XXXX => { &ciddial(${EXTEN:3},${EXTEN},30,TW,Zap/1); } _707254XXXX => { &ciddial(${EXTEN:3},${EXTEN},30,TW,Zap/1); } _707716XXXX => { &ciddial(${EXTEN:3},${EXTEN},30,TW,Zap/1); } _707754XXXX => { &ciddial(${EXTEN:3},${EXTEN},30,TW,Zap/1); } _707574XXXX => { &ciddial(${EXTEN:3},${EXTEN},30,TW,Zap/1); } _NXXNXXXXXX => { &ciddial(1${EXTEN},${EXTEN},30,TW,Zap/1); } _1NXXNXXXXXX => { &ciddial(${EXTEN},${EXTEN:1},30,TW,Zap/1); } _754XXXX => { &ciddial(${EXTEN},707${EXTEN},30,TW,Zap/1); } _574XXXX => { &ciddial(${EXTEN},707${EXTEN},30,TW,Zap/1); } _NXXXXXX => { &ciddial(1707${EXTEN},707${EXTEN},30,TW,Zap/1); } 911 => { &ciddial(911,911,30,TW,Zap/1); } 411 => { &ciddial(411,411,30,TW,Zap/1); } } context workext { ignorepat => 8; ignorepat => 9; includes { parkedcalls; workfirst; force_home; dialFWD; dialiaxtel; dialgoiax; } s => { loopback: Wait(0); } 1 => { Dial(Zap/3&Zap/5,20,tT); } 2 => { Dial(Zap/5&Zap/6,20,tT); } 21 => { Dial(IAX2/seaniax,20,T); } 22 => { Set(CALLERID(num)=1234567890); Set(CALLERID(name)=TestCaller); Dial(Zap/5,20,mP()A(beep)tw); NoOp(here is dialstatus: ${DIALSTATUS}...); goto s|loopback; } 4 => { VoicemailMain(); goto s|loopback; } 5 => { Record(recording:gsm); Background(recording); } 6 => { ZapBarge(); } 760 => { DateTime(); goto s|loopback; } 761 => { ZapBarge(); goto s|loopback; } 765 => { Playback(demo-echotest); Echo(); Playback(demo-echodone); goto s|loopback; } 766 => { Festival(The other thing to watch is neuro-electronics: the ability to interface technology with our neural system: My wife: Sigrid: has had a cochlear implant since 1996. This once profoundly deaf person now uses the phone: recognizes accents: and listens to movies and recorded books.); goto s|loopback; } 767 => { agi(tts-riddle.agi); Background(gsm/what-time-it-is2); SayUnixTime(); goto s|loopback; } 768 => { agi(tts-computer.agi); } 771 => { eagi(eagi-test); agi(my-agi-test); } 772 => { agi(wakeup.agi); } 775 => { if( ${EXTEN}=${EXTEN} ) { BackGround(digits/1); } else { BackGround(digits/0); } if( ${EXTEN}=${LANGUAGE} ) { BackGround(digits/1); } else { BackGround(digits/0); } BackGround(digits/2); } 776 => { Set(TEST=00359889811777); if( ${TEST}= 00359889811777 ) { BackGround(digits/1); } else { BackGround(digits/0); } if( ${TEST}= 00359889811888 ) { BackGround(digits/1); } else { BackGround(digits/0); } Hangup(); } 790 => { MeetMe(790,p); } 792 => { goto pageall|s|begin; } 793 => { #include "include1.ael2" } 795 => { AGI(wakeup.agi); Congestion(); } 797 => { Set(CONFCIDNA=${CALLERID(name)}); Set(CONFCIDNU=${CALLERID(num)}); AGI(callall); AGI(submit-announce.agi); Hangup(); } } context wakeup { 3 => { Dial(Zap/3|30); } 4 => { Dial(Zap/4|30); } 5 => { Dial(Zap/5|30); } 6 => { Dial(Zap/6|30); } 99 => { Dial(IAX2/murfiaxphone|30); } 97 => { Dial(IAX2/ryaniax|30); } 94 => { Dial(IAX2/seaniax|30); } } context announce-all { s => { begin: MeetMe(5555,dtqp); MeetMeAdmin(5555,K); Hangup(); } h => { MeetMeAdmin(5555,K); Hangup(); } } // now include the telemarketer torture scripts! #include "telemarket_torture.ael2" asterisk-1.4.21.2/pbx/ael/ael-test/ref.ael-ntest100000644000175000017500000006340611021261300020624 0ustar maniacmaniac (If you find progress and other non-error messages irritating, you can use -q to suppress them) (You can use the -n option if you aren't interested in seeing all the instructions generated by the compiler) (You can use the -w option to dump extensions.conf format to extensions.conf.aeldump) LOG: lev:2 file:pbx_ael.c line:4131 func: pbx_load_module Starting AEL load process. LOG: lev:2 file:pbx_ael.c line:4138 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:4146 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. LOG: lev:3 file:pbx_ael.c line:2235 func: check_switch_expr Warning: file ./extensions.ael, line 13-13: A default case was automatically added to the switch. LOG: lev:3 file:pbx_ael.c line:2235 func: check_switch_expr Warning: file ./extensions.ael, line 36-36: A default case was automatically added to the switch. LOG: lev:3 file:pbx_ael.c line:2235 func: check_switch_expr Warning: file ./extensions.ael, line 48-48: A default case was automatically added to the switch. LOG: lev:3 file:pbx_ael.c line:2235 func: check_switch_expr Warning: file ./extensions.ael, line 60-60: A default case was automatically added to the switch. LOG: lev:3 file:pbx_ael.c line:2235 func: check_switch_expr Warning: file ./extensions.ael, line 72-72: A default case was automatically added to the switch. LOG: lev:3 file:pbx_ael.c line:2235 func: check_switch_expr Warning: file ./extensions.ael, line 84-84: A default case was automatically added to the switch. LOG: lev:3 file:pbx_ael.c line:2235 func: check_switch_expr Warning: file ./extensions.ael, line 87-87: A default case was automatically added to the switch. LOG: lev:3 file:pbx_ael.c line:2235 func: check_switch_expr Warning: file ./extensions.ael, line 106-106: A default case was automatically added to the switch. LOG: lev:3 file:pbx_ael.c line:2235 func: check_switch_expr Warning: file ./extensions.ael, line 119-119: A default case was automatically added to the switch. LOG: lev:3 file:pbx_ael.c line:2235 func: check_switch_expr Warning: file ./extensions.ael, line 122-122: A default case was automatically added to the switch. LOG: lev:2 file:pbx_ael.c line:4149 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. Executed ast_context_create(conts, name=macro-endsess, registrar=pbx_ael); Executed ast_context_create(conts, name=macro-nullchk, registrar=pbx_ael); Executed ast_context_create(conts, name=macro-endcall, registrar=pbx_ael); Executed ast_context_create(conts, name=macro-endcall2, registrar=pbx_ael); Executed ast_context_create(conts, name=macro-endcall3, registrar=pbx_ael); Executed ast_context_create(conts, name=macro-endcall4, registrar=pbx_ael); Executed ast_context_create(conts, name=macro-endcall5, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endsess, rep=0, exten=s, priority=1, label=(null), callerid=(null), appl=NoOp, data=hithere, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-nullchk, rep=0, exten=s, priority=1, label=(null), callerid=(null), appl=Set, data=type=${ARG1}, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-nullchk, rep=0, exten=s, priority=2, label=(null), callerid=(null), appl=NoOp, data=${type} is this, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall, rep=0, exten=s, priority=1, label=(null), callerid=(null), appl=Set, data=type=${ARG1}, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall, rep=0, exten=s, priority=2, label=(null), callerid=(null), appl=Set, data=~~EXTEN~~=${EXTEN}, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall, rep=0, exten=s, priority=3, label=(null), callerid=(null), appl=Goto, data=sw-1-${type}|10, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall, rep=0, exten=s, priority=4, label=(null), callerid=(null), appl=NoOp, data=Finish switch-endcall-1, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall, rep=0, exten=_sw-1-., priority=10, label=(null), callerid=(null), appl=Goto, data=s|4, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall, rep=0, exten=sw-1-, priority=10, label=(null), callerid=(null), appl=Goto, data=sw-1-.|10, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall, rep=0, exten=sw-1-out, priority=10, label=(null), callerid=(null), appl=Macro, data=nullchk|callid, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall, rep=0, exten=sw-1-out, priority=11, label=(null), callerid=(null), appl=GotoIf, data=$[${testnotnull}]?12:15, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall, rep=0, exten=sw-1-out, priority=12, label=(null), callerid=(null), appl=Macro, data=endsess, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall, rep=0, exten=sw-1-out, priority=13, label=(null), callerid=(null), appl=Goto, data=sw-1-out|ptr1, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall, rep=0, exten=sw-1-out, priority=14, label=(null), callerid=(null), appl=Goto, data=17, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall, rep=0, exten=sw-1-out, priority=15, label=ptr1, callerid=(null), appl=Softhangup, data=${CHANNEL}, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall, rep=0, exten=sw-1-out, priority=16, label=(null), callerid=(null), appl=Goto, data=s|4, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall, rep=0, exten=sw-1-out, priority=17, label=(null), callerid=(null), appl=NoOp, data=Finish if-sw-endcall-out-1-2, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall, rep=0, exten=sw-1-out, priority=18, label=(null), callerid=(null), appl=Noop, data=esac, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall, rep=0, exten=sw-1-out, priority=19, label=(null), callerid=(null), appl=Goto, data=sw-1-.|10, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall2, rep=0, exten=s, priority=1, label=(null), callerid=(null), appl=Set, data=type=${ARG1}, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall2, rep=0, exten=s, priority=2, label=(null), callerid=(null), appl=Set, data=~~EXTEN~~=${EXTEN}, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall2, rep=0, exten=s, priority=3, label=(null), callerid=(null), appl=Goto, data=sw-3-${type}|10, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall2, rep=0, exten=s, priority=4, label=(null), callerid=(null), appl=NoOp, data=Finish switch-endcall2-3, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall2, rep=0, exten=_sw-3-., priority=10, label=(null), callerid=(null), appl=Goto, data=s|4, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall2, rep=0, exten=sw-3-, priority=10, label=(null), callerid=(null), appl=Goto, data=sw-3-.|10, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall2, rep=0, exten=sw-3-out2, priority=10, label=ptr1, callerid=(null), appl=Softhangup, data=${CHANNEL}, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall2, rep=0, exten=sw-3-out2, priority=11, label=(null), callerid=(null), appl=Goto, data=s|4, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall2, rep=0, exten=sw-3-out2, priority=12, label=(null), callerid=(null), appl=Noop, data=esac, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall2, rep=0, exten=sw-3-out2, priority=13, label=(null), callerid=(null), appl=Goto, data=sw-3-.|10, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall2, rep=0, exten=sw-3-out, priority=10, label=(null), callerid=(null), appl=Macro, data=nullchk|callid, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall2, rep=0, exten=sw-3-out, priority=11, label=(null), callerid=(null), appl=GotoIf, data=$[${testnotnull}]?12:14, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall2, rep=0, exten=sw-3-out, priority=12, label=(null), callerid=(null), appl=Macro, data=endsess, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall2, rep=0, exten=sw-3-out, priority=13, label=(null), callerid=(null), appl=Goto, data=sw-3-out2|ptr1, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall2, rep=0, exten=sw-3-out, priority=14, label=(null), callerid=(null), appl=NoOp, data=Finish if-sw-endcall2-out-3-4, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall2, rep=0, exten=sw-3-out, priority=15, label=(null), callerid=(null), appl=Goto, data=sw-3-out2|10, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall3, rep=0, exten=s, priority=1, label=(null), callerid=(null), appl=Set, data=type=${ARG1}, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall3, rep=0, exten=s, priority=2, label=(null), callerid=(null), appl=Set, data=~~EXTEN~~=${EXTEN}, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall3, rep=0, exten=s, priority=3, label=(null), callerid=(null), appl=Goto, data=sw-5-${type}|10, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall3, rep=0, exten=s, priority=4, label=(null), callerid=(null), appl=NoOp, data=Finish switch-endcall3-5, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall3, rep=0, exten=s, priority=5, label=(null), callerid=(null), appl=GotoIf, data=$[${testnotnull}]?6:7, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall3, rep=0, exten=s, priority=6, label=(null), callerid=(null), appl=Goto, data=sw-8-out|ptr1, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall3, rep=0, exten=s, priority=7, label=(null), callerid=(null), appl=NoOp, data=Finish if-endcall3-7, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall3, rep=0, exten=s, priority=8, label=(null), callerid=(null), appl=Goto, data=sw-8-${type}|10, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall3, rep=0, exten=s, priority=9, label=(null), callerid=(null), appl=NoOp, data=Finish switch-endcall3-8, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall3, rep=0, exten=_sw-8-., priority=10, label=(null), callerid=(null), appl=Goto, data=s|9, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall3, rep=0, exten=sw-8-, priority=10, label=(null), callerid=(null), appl=Goto, data=sw-8-.|10, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall3, rep=0, exten=sw-8-out, priority=10, label=(null), callerid=(null), appl=GotoIf, data=$[${testnotnull}]?11:13, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall3, rep=0, exten=sw-8-out, priority=11, label=ptr1, callerid=(null), appl=Softhangup, data=${CHANNEL}, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall3, rep=0, exten=sw-8-out, priority=12, label=(null), callerid=(null), appl=Goto, data=s|9, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall3, rep=0, exten=sw-8-out, priority=13, label=(null), callerid=(null), appl=NoOp, data=Finish if-sw-endcall3-out-8-9, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall3, rep=0, exten=sw-8-out, priority=14, label=(null), callerid=(null), appl=Noop, data=esac, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall3, rep=0, exten=sw-8-out, priority=15, label=(null), callerid=(null), appl=Goto, data=sw-8-.|10, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall3, rep=0, exten=_sw-5-., priority=10, label=(null), callerid=(null), appl=Goto, data=s|4, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall3, rep=0, exten=sw-5-, priority=10, label=(null), callerid=(null), appl=Goto, data=sw-5-.|10, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall3, rep=0, exten=sw-5-out, priority=10, label=(null), callerid=(null), appl=Macro, data=nullchk|callid, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall3, rep=0, exten=sw-5-out, priority=11, label=(null), callerid=(null), appl=GotoIf, data=$[${testnotnull}]?12:14, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall3, rep=0, exten=sw-5-out, priority=12, label=(null), callerid=(null), appl=Macro, data=endsess, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall3, rep=0, exten=sw-5-out, priority=13, label=(null), callerid=(null), appl=Goto, data=sw-8-out|ptr1, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall3, rep=0, exten=sw-5-out, priority=14, label=(null), callerid=(null), appl=NoOp, data=Finish if-sw-endcall3-out-5-6, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall3, rep=0, exten=sw-5-out, priority=15, label=(null), callerid=(null), appl=Noop, data=esac, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall3, rep=0, exten=sw-5-out, priority=16, label=(null), callerid=(null), appl=Goto, data=sw-5-.|10, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall4, rep=0, exten=s, priority=1, label=(null), callerid=(null), appl=Set, data=type=${ARG1}, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall4, rep=0, exten=s, priority=2, label=(null), callerid=(null), appl=Set, data=~~EXTEN~~=${EXTEN}, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall4, rep=0, exten=s, priority=3, label=(null), callerid=(null), appl=Goto, data=sw-10-${type}|10, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall4, rep=0, exten=s, priority=4, label=(null), callerid=(null), appl=NoOp, data=Finish switch-endcall4-10, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall4, rep=0, exten=s, priority=5, label=(null), callerid=(null), appl=GotoIf, data=$[${testnotnull}]?6:7, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall4, rep=0, exten=s, priority=6, label=(null), callerid=(null), appl=Goto, data=sw-14-in|ptr1, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall4, rep=0, exten=s, priority=7, label=(null), callerid=(null), appl=NoOp, data=Finish if-endcall4-12, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall4, rep=0, exten=s, priority=8, label=(null), callerid=(null), appl=Goto, data=sw-13-${type}|10, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall4, rep=0, exten=s, priority=9, label=(null), callerid=(null), appl=NoOp, data=Finish switch-endcall4-13, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall4, rep=0, exten=_sw-13-., priority=10, label=(null), callerid=(null), appl=Goto, data=s|9, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall4, rep=0, exten=sw-13-, priority=10, label=(null), callerid=(null), appl=Goto, data=sw-13-.|10, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall4, rep=0, exten=sw-13-out, priority=10, label=(null), callerid=(null), appl=Set, data=~~EXTEN~~=${EXTEN}, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall4, rep=0, exten=sw-13-out, priority=11, label=(null), callerid=(null), appl=Goto, data=sw-14-${type}|10, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall4, rep=0, exten=sw-13-out, priority=12, label=(null), callerid=(null), appl=NoOp, data=Finish switch-sw-endcall4-out-13-14, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall4, rep=0, exten=sw-13-out, priority=13, label=(null), callerid=(null), appl=Goto, data=sw-13-.|10, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall4, rep=0, exten=_sw-14-., priority=10, label=(null), callerid=(null), appl=Goto, data=sw-13-out|12, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall4, rep=0, exten=sw-14-, priority=10, label=(null), callerid=(null), appl=Goto, data=sw-14-.|10, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall4, rep=0, exten=sw-14-in, priority=10, label=(null), callerid=(null), appl=GotoIf, data=$[${testnotnull}]?11:13, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall4, rep=0, exten=sw-14-in, priority=11, label=ptr1, callerid=(null), appl=Softhangup, data=${CHANNEL}, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall4, rep=0, exten=sw-14-in, priority=12, label=(null), callerid=(null), appl=Goto, data=sw-13-out|12, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall4, rep=0, exten=sw-14-in, priority=13, label=(null), callerid=(null), appl=NoOp, data=Finish if-sw-sw-endcall4-out-13-in-14-15, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall4, rep=0, exten=sw-14-in, priority=14, label=(null), callerid=(null), appl=Noop, data=esac, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall4, rep=0, exten=sw-14-in, priority=15, label=(null), callerid=(null), appl=Goto, data=sw-14-.|10, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall4, rep=0, exten=_sw-10-., priority=10, label=(null), callerid=(null), appl=Goto, data=s|4, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall4, rep=0, exten=sw-10-, priority=10, label=(null), callerid=(null), appl=Goto, data=sw-10-.|10, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall4, rep=0, exten=sw-10-out, priority=10, label=(null), callerid=(null), appl=Macro, data=nullchk|callid, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall4, rep=0, exten=sw-10-out, priority=11, label=(null), callerid=(null), appl=GotoIf, data=$[${testnotnull}]?12:14, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall4, rep=0, exten=sw-10-out, priority=12, label=(null), callerid=(null), appl=Macro, data=endsess, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall4, rep=0, exten=sw-10-out, priority=13, label=(null), callerid=(null), appl=Goto, data=sw-14-in|ptr1, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall4, rep=0, exten=sw-10-out, priority=14, label=(null), callerid=(null), appl=NoOp, data=Finish if-sw-endcall4-out-10-11, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall4, rep=0, exten=sw-10-out, priority=15, label=(null), callerid=(null), appl=Noop, data=esac, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall4, rep=0, exten=sw-10-out, priority=16, label=(null), callerid=(null), appl=Goto, data=sw-10-.|10, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall5, rep=0, exten=s, priority=1, label=(null), callerid=(null), appl=Set, data=type=${ARG1}, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall5, rep=0, exten=s, priority=2, label=(null), callerid=(null), appl=Set, data=~~EXTEN~~=${EXTEN}, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall5, rep=0, exten=s, priority=3, label=(null), callerid=(null), appl=Goto, data=sw-16-${type}|10, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall5, rep=0, exten=s, priority=4, label=(null), callerid=(null), appl=NoOp, data=Finish switch-endcall5-16, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall5, rep=0, exten=s, priority=5, label=(null), callerid=(null), appl=GotoIf, data=$[${testnotnull}]?6:7, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall5, rep=0, exten=s, priority=6, label=(null), callerid=(null), appl=Goto, data=sw-21-in|ptr1, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall5, rep=0, exten=s, priority=7, label=(null), callerid=(null), appl=NoOp, data=Finish if-endcall5-19, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall5, rep=0, exten=s, priority=8, label=(null), callerid=(null), appl=Goto, data=sw-20-${type}|10, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall5, rep=0, exten=s, priority=9, label=(null), callerid=(null), appl=NoOp, data=Finish switch-endcall5-20, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall5, rep=0, exten=_sw-20-., priority=10, label=(null), callerid=(null), appl=Goto, data=s|9, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall5, rep=0, exten=sw-20-, priority=10, label=(null), callerid=(null), appl=Goto, data=sw-20-.|10, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall5, rep=0, exten=sw-20-out, priority=10, label=(null), callerid=(null), appl=Set, data=~~EXTEN~~=${EXTEN}, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall5, rep=0, exten=sw-20-out, priority=11, label=(null), callerid=(null), appl=Goto, data=sw-21-${type}|10, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall5, rep=0, exten=sw-20-out, priority=12, label=(null), callerid=(null), appl=NoOp, data=Finish switch-sw-endcall5-out-20-21, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall5, rep=0, exten=sw-20-out, priority=13, label=(null), callerid=(null), appl=Goto, data=sw-20-.|10, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall5, rep=0, exten=_sw-21-., priority=10, label=(null), callerid=(null), appl=Goto, data=sw-20-out|12, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall5, rep=0, exten=sw-21-, priority=10, label=(null), callerid=(null), appl=Goto, data=sw-21-.|10, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall5, rep=0, exten=sw-21-in, priority=10, label=(null), callerid=(null), appl=GotoIf, data=$[${testnotnull}]?11:13, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall5, rep=0, exten=sw-21-in, priority=11, label=ptr1, callerid=(null), appl=Softhangup, data=${CHANNEL}, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall5, rep=0, exten=sw-21-in, priority=12, label=(null), callerid=(null), appl=Goto, data=sw-20-out|12, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall5, rep=0, exten=sw-21-in, priority=13, label=(null), callerid=(null), appl=NoOp, data=Finish if-sw-sw-endcall5-out-20-in-21-22, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall5, rep=0, exten=sw-21-in, priority=14, label=(null), callerid=(null), appl=Noop, data=esac, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall5, rep=0, exten=sw-21-in, priority=15, label=(null), callerid=(null), appl=Goto, data=sw-21-.|10, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall5, rep=0, exten=_sw-16-., priority=10, label=(null), callerid=(null), appl=Goto, data=s|4, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall5, rep=0, exten=sw-16-, priority=10, label=(null), callerid=(null), appl=Goto, data=sw-16-.|10, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall5, rep=0, exten=sw-16-in, priority=10, label=(null), callerid=(null), appl=Macro, data=nullchk|callid, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall5, rep=0, exten=sw-16-in, priority=11, label=ptr2, callerid=(null), appl=GotoIf, data=$[${testnotnull}]?12:14, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall5, rep=0, exten=sw-16-in, priority=12, label=(null), callerid=(null), appl=Macro, data=endsess, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall5, rep=0, exten=sw-16-in, priority=13, label=(null), callerid=(null), appl=Goto, data=sw-21-in|ptr1, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall5, rep=0, exten=sw-16-in, priority=14, label=(null), callerid=(null), appl=NoOp, data=Finish if-sw-endcall5-in-16-18, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall5, rep=0, exten=sw-16-in, priority=15, label=(null), callerid=(null), appl=Noop, data=esac, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall5, rep=0, exten=sw-16-in, priority=16, label=(null), callerid=(null), appl=Goto, data=sw-16-.|10, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall5, rep=0, exten=sw-16-out, priority=10, label=(null), callerid=(null), appl=Macro, data=nullchk|callid, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall5, rep=0, exten=sw-16-out, priority=11, label=(null), callerid=(null), appl=GotoIf, data=$[${testnotnull}]?12:14, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall5, rep=0, exten=sw-16-out, priority=12, label=(null), callerid=(null), appl=Macro, data=endsess, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall5, rep=0, exten=sw-16-out, priority=13, label=(null), callerid=(null), appl=Goto, data=sw-21-in|ptr1, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall5, rep=0, exten=sw-16-out, priority=14, label=(null), callerid=(null), appl=NoOp, data=Finish if-sw-endcall5-out-16-17, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=macro-endcall5, rep=0, exten=sw-16-out, priority=15, label=(null), callerid=(null), appl=Goto, data=sw-16-in|10, FREE, registrar=pbx_ael); LOG: lev:2 file:pbx_ael.c line:4151 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'. Executed ast_merge_contexts_and_delete(); LOG: lev:2 file:pbx_ael.c line:4154 func: pbx_load_module AEL load process: merged config file name './extensions.ael'. Executed ast_walk_contexts(); LOG: lev:2 file:pbx_ael.c line:4157 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. LOG: lev:4 file:ael2_parse line:543 func: main 7 contexts, 37 extensions, 131 priorities asterisk-1.4.21.2/pbx/ael/ael-test/ref.ael-test110000644000175000017500000000307010660114702020451 0ustar maniacmaniac (If you find progress and other non-error messages irritating, you can use -q to suppress them) (You can use the -w option to dump extensions.conf format to extensions.conf.aeldump) LOG: lev:2 file:pbx_ael.c line:3978 func: pbx_load_module Starting AEL load process. LOG: lev:2 file:pbx_ael.c line:3985 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:3993 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. LOG: lev:4 file:pbx_ael.c line:1114 func: check_label Error: file ./extensions.ael, line 13-13: Duplicate label lab1! Previously defined at file ./extensions.ael, line 8. LOG: lev:3 file:pbx_ael.c line:2234 func: check_switch_expr Warning: file ./extensions.ael, line 32-32: A default case was automatically added to the switch. LOG: lev:3 file:pbx_ael.c line:2234 func: check_switch_expr Warning: file ./extensions.ael, line 44-44: A default case was automatically added to the switch. LOG: lev:3 file:pbx_ael.c line:2234 func: check_switch_expr Warning: file ./extensions.ael, line 47-47: A default case was automatically added to the switch. LOG: lev:4 file:pbx_ael.c line:1114 func: check_label Error: file ./extensions.ael, line 49-49: Duplicate label ptr1! Previously defined at file ./extensions.ael, line 33. LOG: lev:4 file:pbx_ael.c line:4006 func: pbx_load_module Sorry, but 0 syntax errors and 2 semantic errors were detected. It doesn't make sense to compile. LOG: lev:4 file:ael2_parse line:523 func: main 0 contexts, 0 extensions, 0 priorities asterisk-1.4.21.2/pbx/ael/ael-test/ref.ael-test160000644000175000017500000000223310710413372020457 0ustar maniacmaniac (If you find progress and other non-error messages irritating, you can use -q to suppress them) (You can use the -w option to dump extensions.conf format to extensions.conf.aeldump) LOG: lev:2 file:pbx_ael.c line:4090 func: pbx_load_module Starting AEL load process. LOG: lev:2 file:pbx_ael.c line:4097 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:4105 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:4108 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. LOG: lev:3 file:pbx_ael.c line:3685 func: add_extensions This file is Empty! LOG: lev:2 file:pbx_ael.c line:4110 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:4113 func: pbx_load_module AEL load process: merged config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:4116 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. LOG: lev:4 file:ael2_parse line:543 func: main 1 contexts, 0 extensions, 0 priorities asterisk-1.4.21.2/pbx/ael/ael-test/ael-test1/0000755000175000017500000000000011041455740017676 5ustar maniacmaniacasterisk-1.4.21.2/pbx/ael/ael-test/ael-test1/extensions.ael0000644000175000017500000000626410467425741022601 0ustar maniacmaniac macro testdial(number, timeout) { Dial(IAX2/vpconnect-t02/${number},${timeout},${OG_DIAL_FLAGS}); switch (${DIALSTATUS}) { case CHANUNAVAIL: goto dial-trunk2; break; default: NoOp(t02 Unavailable - ${DIALSTATUS}); return; } dial-trunk2: Dial(IAX2/vpconnect-t01/${number},${timeout},${OG_DIAL_FLAGS}); } macro exten-gen(name,pword) { if( ${DB_EXISTS(org/${GroupID}/${name}/secret)} = 0 ) goto other|nomatch|begin; if( ${DB(org/${GroupID}/${name}/secret)}foo != ${pword}foo ) goto other|nomatch|begin; }; context what { who => { random(51) NoOp(This should appear 51% of the time); random( 60 ) { NoOp( This should appear 60% of the time ); } else { random(75) { NoOp( This should appear 30% of the time! ); } else { NoOp( This should appear 10% of the time! ); } } } } context other { nomatch => { begin: NoOp(Hello!); switch(${DIALSTATUS}) { case BUSY: NoOp(wow); case TORTURE: NoOp(woow); }; NoOp(woohoo); }; }; context testloop { includes { other|16:00-23:59|m0n-fri|*|*; }; 1 => { for (x=0; ${x} < 3; x=${x} + 1) { Verbose(x is ${x} !); if( ${x} = 1 ) continue; if( ${x} = 2 ) break; }; ifTime(14:00-25:00|sat-sun|*|*) { BackGround(Hello); } else BackGround(Sorry); NoOp(This is a totally useless NOOP); }; 2 => { y=10; while (${y} >= 0) { Verbose(y is ${y} !); if( ${y} = 1 ) continue; if( ${y} = 2 ) break; if( ${y} = 3 ) return; y=${y}-1; }; }; regexten hint(nasty/Thingy&nasty/Thingamabob) 3 => { for (x=0; ${x} < 3; x=${x} + 1) { Verbose(x is ${x} !); if( ${x} = 4 ) break; if( ${x} = 5 ) continue; if( ${x} = 6 ) return; y=10; while (${y} >= 0) { Verbose(y is ${y} !); if( ${y} = 4 ) break; if( ${y} = 5 ) continue; if( ${y} = 6 ) return; y=${y}-1; }; }; }; 4 => { y=10; while (${y} >= 0) { Verbose(y is ${y} !); if( ${y} = 4 ) break; if( ${y} = 5 ) continue; if( ${y} = 6 ) return; for (x=0; ${x} < 3; x=${x} + 1) { Verbose(x is ${x} !); if( ${x} = 4 ) break; if( ${x} = 5 ) continue; if( ${x} = 6 ) return; for (z=0; ${z} < 17; z=${z} + 1) { Verbose(z is ${z} !); Verbose(z is ${z} !); if( ${z} = 4 ) break; if( ${z} = 5 ) continue; if( ${z} = 6 ) return; Verbose(z is ${z} !); Verbose(z is ${z} !); }; }; y=${y}-1; }; }; 5 => { &exten-gen(axel,brain); }; }; asterisk-1.4.21.2/pbx/ael/ael-test/ael-ntest22/0000755000175000017500000000000011041455737020145 5ustar maniacmaniacasterisk-1.4.21.2/pbx/ael/ael-test/ael-ntest22/t3/0000755000175000017500000000000011041455737020473 5ustar maniacmaniacasterisk-1.4.21.2/pbx/ael/ael-test/ael-ntest22/t3/j.ael0000644000175000017500000000005310677554425021415 0ustar maniacmaniac context j { 567 => NoOp(hi there, j); } asterisk-1.4.21.2/pbx/ael/ael-test/ael-ntest22/t3/i.ael0000644000175000017500000000005110677554425021412 0ustar maniacmaniaccontext i { 134 => NoOp(hi there, i); } asterisk-1.4.21.2/pbx/ael/ael-test/ael-ntest22/t3/g.ael0000644000175000017500000000005110677554425021410 0ustar maniacmaniaccontext g { 134 => NoOp(hi there, g); } asterisk-1.4.21.2/pbx/ael/ael-test/ael-ntest22/t3/h.ael0000644000175000017500000000005210677554425021412 0ustar maniacmaniac context h { 456 => NoOp(hithere, h); } asterisk-1.4.21.2/pbx/ael/ael-test/ael-ntest22/t2/0000755000175000017500000000000011041455737020472 5ustar maniacmaniacasterisk-1.4.21.2/pbx/ael/ael-test/ael-ntest22/t2/d.ael0000644000175000017500000000005110677554425021404 0ustar maniacmaniaccontext d { 134 => NoOp(hi there, d); } asterisk-1.4.21.2/pbx/ael/ael-test/ael-ntest22/t2/e.ael0000644000175000017500000000005210677554425021406 0ustar maniacmaniac context e { 456 => NoOp(hithere, e); } asterisk-1.4.21.2/pbx/ael/ael-test/ael-ntest22/t2/f.ael0000644000175000017500000000012210677554425021405 0ustar maniacmaniac#include "qq.ael" context f { 567 => NoOp(hi there, f); } #include "t3/*.ael" asterisk-1.4.21.2/pbx/ael/ael-test/ael-ntest22/t1/0000755000175000017500000000000011041455737020471 5ustar maniacmaniacasterisk-1.4.21.2/pbx/ael/ael-test/ael-ntest22/t1/a.ael0000644000175000017500000000005110677554425021400 0ustar maniacmaniaccontext a { 134 => NoOp(hi there, a); } asterisk-1.4.21.2/pbx/ael/ael-test/ael-ntest22/t1/b.ael0000644000175000017500000000005210677554425021402 0ustar maniacmaniac context b { 456 => NoOp(hithere, b); } asterisk-1.4.21.2/pbx/ael/ael-test/ael-ntest22/t1/c.ael0000644000175000017500000000015210677554425021404 0ustar maniacmaniac context c { 567 => NoOp(hi there, c); } #include "t2/*.ael" context w { 890 => NoOp(hi there, w); } asterisk-1.4.21.2/pbx/ael/ael-test/ael-ntest22/qq.ael0000644000175000017500000000005510677554425021261 0ustar maniacmaniac context qq { 567 => NoOp(hi there, qq); } asterisk-1.4.21.2/pbx/ael/ael-test/ael-ntest22/extensions.ael0000644000175000017500000000013210677554425023033 0ustar maniacmaniac#include "t1/*.ael" context z { 123 => NoOp(hi there, z); 124 => NoOp(hi there, z); } asterisk-1.4.21.2/pbx/ael/ael-test/ref.ael-ntest120000644000175000017500000000630410511011024020617 0ustar maniacmaniac (If you find progress and other non-error messages irritating, you can use -q to suppress them) (You can use the -n option if you aren't interested in seeing all the instructions generated by the compiler) (You can use the -w option to dump extensions.conf format to extensions.conf.aeldump) LOG: lev:2 file:pbx_ael.c line:3803 func: pbx_load_module Starting AEL load process. LOG: lev:2 file:pbx_ael.c line:3810 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:3818 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:3821 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. Executed ast_context_create(conts, name=test1, registrar=pbx_ael); Executed ast_add_extension2(context=test1, rep=0, exten=771, priority=1, label=(null), callerid=(null), appl=Set, data=i=$[0], FREE, registrar=pbx_ael); Executed ast_add_extension2(context=test1, rep=0, exten=771, priority=2, label=(null), callerid=(null), appl=GotoIf, data=$[ ${i} <= 3]?3:6, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=test1, rep=0, exten=771, priority=3, label=(null), callerid=(null), appl=NoOp, data=i is '${i}', FREE, registrar=pbx_ael); Executed ast_add_extension2(context=test1, rep=0, exten=771, priority=4, label=(null), callerid=(null), appl=Set, data=i=$[ ${i} + 1 ], FREE, registrar=pbx_ael); Executed ast_add_extension2(context=test1, rep=0, exten=771, priority=5, label=(null), callerid=(null), appl=Goto, data=2, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=test1, rep=0, exten=771, priority=6, label=(null), callerid=(null), appl=NoOp, data=Finish for-test1-1, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=test1, rep=0, exten=772, priority=1, label=(null), callerid=(null), appl=Set, data=i=$[0], FREE, registrar=pbx_ael); Executed ast_add_extension2(context=test1, rep=0, exten=772, priority=2, label=(null), callerid=(null), appl=GotoIf, data=$[ ${i} <= 3]?3:6, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=test1, rep=0, exten=772, priority=3, label=(null), callerid=(null), appl=NoOp, data=i is '${i}', FREE, registrar=pbx_ael); Executed ast_add_extension2(context=test1, rep=0, exten=772, priority=4, label=(null), callerid=(null), appl=Set, data=i=$[ ${i} + 1 ], FREE, registrar=pbx_ael); Executed ast_add_extension2(context=test1, rep=0, exten=772, priority=5, label=(null), callerid=(null), appl=Goto, data=2, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=test1, rep=0, exten=772, priority=6, label=(null), callerid=(null), appl=NoOp, data=Finish for-test1-2, FREE, registrar=pbx_ael); LOG: lev:2 file:pbx_ael.c line:3823 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'. Executed ast_merge_contexts_and_delete(); LOG: lev:2 file:pbx_ael.c line:3826 func: pbx_load_module AEL load process: merged config file name './extensions.ael'. Executed ast_walk_contexts(); LOG: lev:2 file:pbx_ael.c line:3829 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. LOG: lev:4 file:ael2_parse line:479 func: main 1 contexts, 2 extensions, 12 priorities asterisk-1.4.21.2/pbx/ael/ael-test/ref.ael-ntest220000644000175000017500000001246610677554425020664 0ustar maniacmaniac (If you find progress and other non-error messages irritating, you can use -q to suppress them) (You can use the -n option if you aren't interested in seeing all the instructions generated by the compiler) (You can use the -w option to dump extensions.conf format to extensions.conf.aeldump) LOG: lev:2 file:pbx_ael.c line:4048 func: pbx_load_module Starting AEL load process. LOG: lev:2 file:pbx_ael.c line:4055 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. LOG: lev:2 file:ael.flex line:654 func: setup_filestack --Read in included file ./t1/a.ael, 41 chars LOG: lev:2 file:ael.flex line:654 func: setup_filestack --Read in included file ./t1/b.ael, 42 chars LOG: lev:2 file:ael.flex line:654 func: setup_filestack --Read in included file ./t1/c.ael, 106 chars LOG: lev:2 file:ael.flex line:654 func: setup_filestack --Read in included file ./t2/d.ael, 41 chars LOG: lev:2 file:ael.flex line:654 func: setup_filestack --Read in included file ./t2/e.ael, 42 chars LOG: lev:2 file:ael.flex line:654 func: setup_filestack --Read in included file ./t2/f.ael, 82 chars LOG: lev:2 file:ael.flex line:654 func: setup_filestack --Read in included file ./qq.ael, 45 chars LOG: lev:2 file:ael.flex line:654 func: setup_filestack --Read in included file ./t3/g.ael, 41 chars LOG: lev:2 file:ael.flex line:654 func: setup_filestack --Read in included file ./t3/h.ael, 42 chars LOG: lev:2 file:ael.flex line:654 func: setup_filestack --Read in included file ./t3/i.ael, 41 chars LOG: lev:2 file:ael.flex line:654 func: setup_filestack --Read in included file ./t3/j.ael, 43 chars LOG: lev:2 file:pbx_ael.c line:4063 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:4066 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. Executed ast_context_create(conts, name=a, registrar=pbx_ael); Executed ast_context_create(conts, name=b, registrar=pbx_ael); Executed ast_context_create(conts, name=c, registrar=pbx_ael); Executed ast_context_create(conts, name=d, registrar=pbx_ael); Executed ast_context_create(conts, name=e, registrar=pbx_ael); Executed ast_context_create(conts, name=qq, registrar=pbx_ael); Executed ast_context_create(conts, name=f, registrar=pbx_ael); Executed ast_context_create(conts, name=g, registrar=pbx_ael); Executed ast_context_create(conts, name=h, registrar=pbx_ael); Executed ast_context_create(conts, name=i, registrar=pbx_ael); Executed ast_context_create(conts, name=j, registrar=pbx_ael); Executed ast_context_create(conts, name=w, registrar=pbx_ael); Executed ast_context_create(conts, name=z, registrar=pbx_ael); Executed ast_add_extension2(context=a, rep=0, exten=134, priority=1, label=(null), callerid=(null), appl=NoOp, data=hi there| a, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=b, rep=0, exten=456, priority=1, label=(null), callerid=(null), appl=NoOp, data=hithere| b, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=c, rep=0, exten=567, priority=1, label=(null), callerid=(null), appl=NoOp, data=hi there| c, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=d, rep=0, exten=134, priority=1, label=(null), callerid=(null), appl=NoOp, data=hi there| d, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=e, rep=0, exten=456, priority=1, label=(null), callerid=(null), appl=NoOp, data=hithere| e, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=qq, rep=0, exten=567, priority=1, label=(null), callerid=(null), appl=NoOp, data=hi there| qq, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=f, rep=0, exten=567, priority=1, label=(null), callerid=(null), appl=NoOp, data=hi there| f, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=g, rep=0, exten=134, priority=1, label=(null), callerid=(null), appl=NoOp, data=hi there| g, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=h, rep=0, exten=456, priority=1, label=(null), callerid=(null), appl=NoOp, data=hithere| h, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=i, rep=0, exten=134, priority=1, label=(null), callerid=(null), appl=NoOp, data=hi there| i, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=j, rep=0, exten=567, priority=1, label=(null), callerid=(null), appl=NoOp, data=hi there| j, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=w, rep=0, exten=890, priority=1, label=(null), callerid=(null), appl=NoOp, data=hi there| w, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=z, rep=0, exten=123, priority=1, label=(null), callerid=(null), appl=NoOp, data=hi there| z, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=z, rep=0, exten=124, priority=1, label=(null), callerid=(null), appl=NoOp, data=hi there| z, FREE, registrar=pbx_ael); LOG: lev:2 file:pbx_ael.c line:4068 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'. Executed ast_merge_contexts_and_delete(); LOG: lev:2 file:pbx_ael.c line:4071 func: pbx_load_module AEL load process: merged config file name './extensions.ael'. Executed ast_walk_contexts(); LOG: lev:2 file:pbx_ael.c line:4074 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. LOG: lev:4 file:ael2_parse line:527 func: main 13 contexts, 13 extensions, 14 priorities asterisk-1.4.21.2/pbx/ael/ael-test/ref.ael-vtest130000644000175000017500000032562011021261300020636 0ustar maniacmaniac[globals] static=yes writeprotect=yes CONSOLE=Console/dsp IAXINFO=murf:tlhfckoct FWDNUMBER=544788 FWDCIDNAME="Joe-Worker" FWDPASSWORD=zingledoodle FWDRINGS=Zap/6 FWDVMBOX=1 [macro-std-exten] exten => s,1,Set(ext=${ARG1}) exten => s,2,Set(dev=${ARG2}) exten => s,3,Dial(${dev}/${ext}|20) exten => s,4,Set(~~EXTEN~~=${EXTEN}) exten => s,5,Goto(sw-1-${DIALSTATUS}|10) exten => s,6,NoOp(Finish switch-std-exten-1) exten => a,1,VoiceMailMain(${ext}) exten => _sw-1-.,10,Voicemail(u${ext}) exten => _sw-1-.,11,Goto(s|6) exten => sw-1-,10,Goto(sw-1-.|10) exten => sw-1-ANSWER,10,Goto(s|6) exten => sw-1-NOANSWER,10,Voicemail(u${ext}) exten => sw-1-NOANSWER,11,Goto(s|6) exten => sw-1-BUSY,10,Voicemail(b${ext}) exten => sw-1-BUSY,11,Goto(s|6) [macro-std-priv-exten_1] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-3-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_1-3) exten => _sw-3-.,10,Voicemail(u${ext}) exten => _sw-3-.,11,Goto(s|11) exten => sw-3-,10,Goto(sw-3-.|10) exten => sw-3-NOANSWER,10,Voicemail(u${ext}) exten => sw-3-NOANSWER,11,Goto(s|11) exten => sw-3-ANSWER,10,Goto(s|11) exten => sw-3-BUSY,10,Voicemail(b${ext}) exten => sw-3-BUSY,11,Goto(s|11) exten => sw-3-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-3-DONTCALL,11,Goto(s|11) exten => sw-3-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-3-TORTURE,11,Goto(s|11) [macro-std-priv-exten_2] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-4-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_2-4) exten => _sw-4-.,10,Voicemail(u${ext}) exten => _sw-4-.,11,Goto(s|11) exten => sw-4-,10,Goto(sw-4-.|10) exten => sw-4-NOANSWER,10,Voicemail(u${ext}) exten => sw-4-NOANSWER,11,Goto(s|11) exten => sw-4-ANSWER,10,Goto(s|11) exten => sw-4-BUSY,10,Voicemail(b${ext}) exten => sw-4-BUSY,11,Goto(s|11) exten => sw-4-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-4-DONTCALL,11,Goto(s|11) exten => sw-4-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-4-TORTURE,11,Goto(s|11) [macro-std-priv-exten_3] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-5-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_3-5) exten => _sw-5-.,10,Voicemail(u${ext}) exten => _sw-5-.,11,Goto(s|11) exten => sw-5-,10,Goto(sw-5-.|10) exten => sw-5-NOANSWER,10,Voicemail(u${ext}) exten => sw-5-NOANSWER,11,Goto(s|11) exten => sw-5-ANSWER,10,Goto(s|11) exten => sw-5-BUSY,10,Voicemail(b${ext}) exten => sw-5-BUSY,11,Goto(s|11) exten => sw-5-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-5-DONTCALL,11,Goto(s|11) exten => sw-5-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-5-TORTURE,11,Goto(s|11) [macro-std-priv-exten_4] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-6-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_4-6) exten => _sw-6-.,10,Voicemail(u${ext}) exten => _sw-6-.,11,Goto(s|11) exten => sw-6-,10,Goto(sw-6-.|10) exten => sw-6-NOANSWER,10,Voicemail(u${ext}) exten => sw-6-NOANSWER,11,Goto(s|11) exten => sw-6-ANSWER,10,Goto(s|11) exten => sw-6-BUSY,10,Voicemail(b${ext}) exten => sw-6-BUSY,11,Goto(s|11) exten => sw-6-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-6-DONTCALL,11,Goto(s|11) exten => sw-6-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-6-TORTURE,11,Goto(s|11) [macro-std-priv-exten_5] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-7-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_5-7) exten => _sw-7-.,10,Voicemail(u${ext}) exten => _sw-7-.,11,Goto(s|11) exten => sw-7-,10,Goto(sw-7-.|10) exten => sw-7-NOANSWER,10,Voicemail(u${ext}) exten => sw-7-NOANSWER,11,Goto(s|11) exten => sw-7-ANSWER,10,Goto(s|11) exten => sw-7-BUSY,10,Voicemail(b${ext}) exten => sw-7-BUSY,11,Goto(s|11) exten => sw-7-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-7-DONTCALL,11,Goto(s|11) exten => sw-7-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-7-TORTURE,11,Goto(s|11) [macro-std-priv-exten_6] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-8-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_6-8) exten => _sw-8-.,10,Voicemail(u${ext}) exten => _sw-8-.,11,Goto(s|11) exten => sw-8-,10,Goto(sw-8-.|10) exten => sw-8-NOANSWER,10,Voicemail(u${ext}) exten => sw-8-NOANSWER,11,Goto(s|11) exten => sw-8-ANSWER,10,Goto(s|11) exten => sw-8-BUSY,10,Voicemail(b${ext}) exten => sw-8-BUSY,11,Goto(s|11) exten => sw-8-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-8-DONTCALL,11,Goto(s|11) exten => sw-8-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-8-TORTURE,11,Goto(s|11) [macro-std-priv-exten_7] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-9-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_7-9) exten => _sw-9-.,10,Voicemail(u${ext}) exten => _sw-9-.,11,Goto(s|11) exten => sw-9-,10,Goto(sw-9-.|10) exten => sw-9-NOANSWER,10,Voicemail(u${ext}) exten => sw-9-NOANSWER,11,Goto(s|11) exten => sw-9-ANSWER,10,Goto(s|11) exten => sw-9-BUSY,10,Voicemail(b${ext}) exten => sw-9-BUSY,11,Goto(s|11) exten => sw-9-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-9-DONTCALL,11,Goto(s|11) exten => sw-9-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-9-TORTURE,11,Goto(s|11) [macro-std-priv-exten_8] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-10-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_8-10) exten => _sw-10-.,10,Voicemail(u${ext}) exten => _sw-10-.,11,Goto(s|11) exten => sw-10-,10,Goto(sw-10-.|10) exten => sw-10-NOANSWER,10,Voicemail(u${ext}) exten => sw-10-NOANSWER,11,Goto(s|11) exten => sw-10-ANSWER,10,Goto(s|11) exten => sw-10-BUSY,10,Voicemail(b${ext}) exten => sw-10-BUSY,11,Goto(s|11) exten => sw-10-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-10-DONTCALL,11,Goto(s|11) exten => sw-10-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-10-TORTURE,11,Goto(s|11) [macro-std-priv-exten_9] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-11-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_9-11) exten => _sw-11-.,10,Voicemail(u${ext}) exten => _sw-11-.,11,Goto(s|11) exten => sw-11-,10,Goto(sw-11-.|10) exten => sw-11-NOANSWER,10,Voicemail(u${ext}) exten => sw-11-NOANSWER,11,Goto(s|11) exten => sw-11-ANSWER,10,Goto(s|11) exten => sw-11-BUSY,10,Voicemail(b${ext}) exten => sw-11-BUSY,11,Goto(s|11) exten => sw-11-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-11-DONTCALL,11,Goto(s|11) exten => sw-11-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-11-TORTURE,11,Goto(s|11) [macro-std-priv-exten_10] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-12-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_10-12) exten => _sw-12-.,10,Voicemail(u${ext}) exten => _sw-12-.,11,Goto(s|11) exten => sw-12-,10,Goto(sw-12-.|10) exten => sw-12-NOANSWER,10,Voicemail(u${ext}) exten => sw-12-NOANSWER,11,Goto(s|11) exten => sw-12-ANSWER,10,Goto(s|11) exten => sw-12-BUSY,10,Voicemail(b${ext}) exten => sw-12-BUSY,11,Goto(s|11) exten => sw-12-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-12-DONTCALL,11,Goto(s|11) exten => sw-12-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-12-TORTURE,11,Goto(s|11) [macro-std-priv-exten_11] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-13-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_11-13) exten => _sw-13-.,10,Voicemail(u${ext}) exten => _sw-13-.,11,Goto(s|11) exten => sw-13-,10,Goto(sw-13-.|10) exten => sw-13-NOANSWER,10,Voicemail(u${ext}) exten => sw-13-NOANSWER,11,Goto(s|11) exten => sw-13-ANSWER,10,Goto(s|11) exten => sw-13-BUSY,10,Voicemail(b${ext}) exten => sw-13-BUSY,11,Goto(s|11) exten => sw-13-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-13-DONTCALL,11,Goto(s|11) exten => sw-13-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-13-TORTURE,11,Goto(s|11) [macro-std-priv-exten_12] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-14-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_12-14) exten => _sw-14-.,10,Voicemail(u${ext}) exten => _sw-14-.,11,Goto(s|11) exten => sw-14-,10,Goto(sw-14-.|10) exten => sw-14-NOANSWER,10,Voicemail(u${ext}) exten => sw-14-NOANSWER,11,Goto(s|11) exten => sw-14-ANSWER,10,Goto(s|11) exten => sw-14-BUSY,10,Voicemail(b${ext}) exten => sw-14-BUSY,11,Goto(s|11) exten => sw-14-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-14-DONTCALL,11,Goto(s|11) exten => sw-14-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-14-TORTURE,11,Goto(s|11) [macro-std-priv-exten_13] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-15-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_13-15) exten => _sw-15-.,10,Voicemail(u${ext}) exten => _sw-15-.,11,Goto(s|11) exten => sw-15-,10,Goto(sw-15-.|10) exten => sw-15-NOANSWER,10,Voicemail(u${ext}) exten => sw-15-NOANSWER,11,Goto(s|11) exten => sw-15-ANSWER,10,Goto(s|11) exten => sw-15-BUSY,10,Voicemail(b${ext}) exten => sw-15-BUSY,11,Goto(s|11) exten => sw-15-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-15-DONTCALL,11,Goto(s|11) exten => sw-15-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-15-TORTURE,11,Goto(s|11) [macro-std-priv-exten_14] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-16-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_14-16) exten => _sw-16-.,10,Voicemail(u${ext}) exten => _sw-16-.,11,Goto(s|11) exten => sw-16-,10,Goto(sw-16-.|10) exten => sw-16-NOANSWER,10,Voicemail(u${ext}) exten => sw-16-NOANSWER,11,Goto(s|11) exten => sw-16-ANSWER,10,Goto(s|11) exten => sw-16-BUSY,10,Voicemail(b${ext}) exten => sw-16-BUSY,11,Goto(s|11) exten => sw-16-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-16-DONTCALL,11,Goto(s|11) exten => sw-16-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-16-TORTURE,11,Goto(s|11) [macro-std-priv-exten_15] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-17-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_15-17) exten => _sw-17-.,10,Voicemail(u${ext}) exten => _sw-17-.,11,Goto(s|11) exten => sw-17-,10,Goto(sw-17-.|10) exten => sw-17-NOANSWER,10,Voicemail(u${ext}) exten => sw-17-NOANSWER,11,Goto(s|11) exten => sw-17-ANSWER,10,Goto(s|11) exten => sw-17-BUSY,10,Voicemail(b${ext}) exten => sw-17-BUSY,11,Goto(s|11) exten => sw-17-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-17-DONTCALL,11,Goto(s|11) exten => sw-17-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-17-TORTURE,11,Goto(s|11) [macro-std-priv-exten_16] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-18-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_16-18) exten => _sw-18-.,10,Voicemail(u${ext}) exten => _sw-18-.,11,Goto(s|11) exten => sw-18-,10,Goto(sw-18-.|10) exten => sw-18-NOANSWER,10,Voicemail(u${ext}) exten => sw-18-NOANSWER,11,Goto(s|11) exten => sw-18-ANSWER,10,Goto(s|11) exten => sw-18-BUSY,10,Voicemail(b${ext}) exten => sw-18-BUSY,11,Goto(s|11) exten => sw-18-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-18-DONTCALL,11,Goto(s|11) exten => sw-18-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-18-TORTURE,11,Goto(s|11) [macro-std-priv-exten_17] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-19-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_17-19) exten => _sw-19-.,10,Voicemail(u${ext}) exten => _sw-19-.,11,Goto(s|11) exten => sw-19-,10,Goto(sw-19-.|10) exten => sw-19-NOANSWER,10,Voicemail(u${ext}) exten => sw-19-NOANSWER,11,Goto(s|11) exten => sw-19-ANSWER,10,Goto(s|11) exten => sw-19-BUSY,10,Voicemail(b${ext}) exten => sw-19-BUSY,11,Goto(s|11) exten => sw-19-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-19-DONTCALL,11,Goto(s|11) exten => sw-19-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-19-TORTURE,11,Goto(s|11) [macro-std-priv-exten_18] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-20-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_18-20) exten => _sw-20-.,10,Voicemail(u${ext}) exten => _sw-20-.,11,Goto(s|11) exten => sw-20-,10,Goto(sw-20-.|10) exten => sw-20-NOANSWER,10,Voicemail(u${ext}) exten => sw-20-NOANSWER,11,Goto(s|11) exten => sw-20-ANSWER,10,Goto(s|11) exten => sw-20-BUSY,10,Voicemail(b${ext}) exten => sw-20-BUSY,11,Goto(s|11) exten => sw-20-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-20-DONTCALL,11,Goto(s|11) exten => sw-20-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-20-TORTURE,11,Goto(s|11) [macro-std-priv-exten_19] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-21-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_19-21) exten => _sw-21-.,10,Voicemail(u${ext}) exten => _sw-21-.,11,Goto(s|11) exten => sw-21-,10,Goto(sw-21-.|10) exten => sw-21-NOANSWER,10,Voicemail(u${ext}) exten => sw-21-NOANSWER,11,Goto(s|11) exten => sw-21-ANSWER,10,Goto(s|11) exten => sw-21-BUSY,10,Voicemail(b${ext}) exten => sw-21-BUSY,11,Goto(s|11) exten => sw-21-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-21-DONTCALL,11,Goto(s|11) exten => sw-21-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-21-TORTURE,11,Goto(s|11) [macro-std-priv-exten_20] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-22-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_20-22) exten => _sw-22-.,10,Voicemail(u${ext}) exten => _sw-22-.,11,Goto(s|11) exten => sw-22-,10,Goto(sw-22-.|10) exten => sw-22-NOANSWER,10,Voicemail(u${ext}) exten => sw-22-NOANSWER,11,Goto(s|11) exten => sw-22-ANSWER,10,Goto(s|11) exten => sw-22-BUSY,10,Voicemail(b${ext}) exten => sw-22-BUSY,11,Goto(s|11) exten => sw-22-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-22-DONTCALL,11,Goto(s|11) exten => sw-22-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-22-TORTURE,11,Goto(s|11) [macro-std-priv-exten_21] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-23-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_21-23) exten => _sw-23-.,10,Voicemail(u${ext}) exten => _sw-23-.,11,Goto(s|11) exten => sw-23-,10,Goto(sw-23-.|10) exten => sw-23-NOANSWER,10,Voicemail(u${ext}) exten => sw-23-NOANSWER,11,Goto(s|11) exten => sw-23-ANSWER,10,Goto(s|11) exten => sw-23-BUSY,10,Voicemail(b${ext}) exten => sw-23-BUSY,11,Goto(s|11) exten => sw-23-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-23-DONTCALL,11,Goto(s|11) exten => sw-23-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-23-TORTURE,11,Goto(s|11) [macro-std-priv-exten_22] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-24-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_22-24) exten => _sw-24-.,10,Voicemail(u${ext}) exten => _sw-24-.,11,Goto(s|11) exten => sw-24-,10,Goto(sw-24-.|10) exten => sw-24-NOANSWER,10,Voicemail(u${ext}) exten => sw-24-NOANSWER,11,Goto(s|11) exten => sw-24-ANSWER,10,Goto(s|11) exten => sw-24-BUSY,10,Voicemail(b${ext}) exten => sw-24-BUSY,11,Goto(s|11) exten => sw-24-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-24-DONTCALL,11,Goto(s|11) exten => sw-24-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-24-TORTURE,11,Goto(s|11) [macro-std-priv-exten_23] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-25-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_23-25) exten => _sw-25-.,10,Voicemail(u${ext}) exten => _sw-25-.,11,Goto(s|11) exten => sw-25-,10,Goto(sw-25-.|10) exten => sw-25-NOANSWER,10,Voicemail(u${ext}) exten => sw-25-NOANSWER,11,Goto(s|11) exten => sw-25-ANSWER,10,Goto(s|11) exten => sw-25-BUSY,10,Voicemail(b${ext}) exten => sw-25-BUSY,11,Goto(s|11) exten => sw-25-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-25-DONTCALL,11,Goto(s|11) exten => sw-25-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-25-TORTURE,11,Goto(s|11) [macro-std-priv-exten_24] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-26-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_24-26) exten => _sw-26-.,10,Voicemail(u${ext}) exten => _sw-26-.,11,Goto(s|11) exten => sw-26-,10,Goto(sw-26-.|10) exten => sw-26-NOANSWER,10,Voicemail(u${ext}) exten => sw-26-NOANSWER,11,Goto(s|11) exten => sw-26-ANSWER,10,Goto(s|11) exten => sw-26-BUSY,10,Voicemail(b${ext}) exten => sw-26-BUSY,11,Goto(s|11) exten => sw-26-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-26-DONTCALL,11,Goto(s|11) exten => sw-26-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-26-TORTURE,11,Goto(s|11) [macro-std-priv-exten_25] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-27-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_25-27) exten => _sw-27-.,10,Voicemail(u${ext}) exten => _sw-27-.,11,Goto(s|11) exten => sw-27-,10,Goto(sw-27-.|10) exten => sw-27-NOANSWER,10,Voicemail(u${ext}) exten => sw-27-NOANSWER,11,Goto(s|11) exten => sw-27-ANSWER,10,Goto(s|11) exten => sw-27-BUSY,10,Voicemail(b${ext}) exten => sw-27-BUSY,11,Goto(s|11) exten => sw-27-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-27-DONTCALL,11,Goto(s|11) exten => sw-27-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-27-TORTURE,11,Goto(s|11) [macro-std-priv-exten_26] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-28-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_26-28) exten => _sw-28-.,10,Voicemail(u${ext}) exten => _sw-28-.,11,Goto(s|11) exten => sw-28-,10,Goto(sw-28-.|10) exten => sw-28-NOANSWER,10,Voicemail(u${ext}) exten => sw-28-NOANSWER,11,Goto(s|11) exten => sw-28-ANSWER,10,Goto(s|11) exten => sw-28-BUSY,10,Voicemail(b${ext}) exten => sw-28-BUSY,11,Goto(s|11) exten => sw-28-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-28-DONTCALL,11,Goto(s|11) exten => sw-28-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-28-TORTURE,11,Goto(s|11) [macro-std-priv-exten_27] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-29-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_27-29) exten => _sw-29-.,10,Voicemail(u${ext}) exten => _sw-29-.,11,Goto(s|11) exten => sw-29-,10,Goto(sw-29-.|10) exten => sw-29-NOANSWER,10,Voicemail(u${ext}) exten => sw-29-NOANSWER,11,Goto(s|11) exten => sw-29-ANSWER,10,Goto(s|11) exten => sw-29-BUSY,10,Voicemail(b${ext}) exten => sw-29-BUSY,11,Goto(s|11) exten => sw-29-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-29-DONTCALL,11,Goto(s|11) exten => sw-29-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-29-TORTURE,11,Goto(s|11) [macro-std-priv-exten_28] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-30-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_28-30) exten => _sw-30-.,10,Voicemail(u${ext}) exten => _sw-30-.,11,Goto(s|11) exten => sw-30-,10,Goto(sw-30-.|10) exten => sw-30-NOANSWER,10,Voicemail(u${ext}) exten => sw-30-NOANSWER,11,Goto(s|11) exten => sw-30-ANSWER,10,Goto(s|11) exten => sw-30-BUSY,10,Voicemail(b${ext}) exten => sw-30-BUSY,11,Goto(s|11) exten => sw-30-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-30-DONTCALL,11,Goto(s|11) exten => sw-30-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-30-TORTURE,11,Goto(s|11) [macro-std-priv-exten_29] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-31-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_29-31) exten => _sw-31-.,10,Voicemail(u${ext}) exten => _sw-31-.,11,Goto(s|11) exten => sw-31-,10,Goto(sw-31-.|10) exten => sw-31-NOANSWER,10,Voicemail(u${ext}) exten => sw-31-NOANSWER,11,Goto(s|11) exten => sw-31-ANSWER,10,Goto(s|11) exten => sw-31-BUSY,10,Voicemail(b${ext}) exten => sw-31-BUSY,11,Goto(s|11) exten => sw-31-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-31-DONTCALL,11,Goto(s|11) exten => sw-31-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-31-TORTURE,11,Goto(s|11) [macro-std-priv-exten_30] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-32-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_30-32) exten => _sw-32-.,10,Voicemail(u${ext}) exten => _sw-32-.,11,Goto(s|11) exten => sw-32-,10,Goto(sw-32-.|10) exten => sw-32-NOANSWER,10,Voicemail(u${ext}) exten => sw-32-NOANSWER,11,Goto(s|11) exten => sw-32-ANSWER,10,Goto(s|11) exten => sw-32-BUSY,10,Voicemail(b${ext}) exten => sw-32-BUSY,11,Goto(s|11) exten => sw-32-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-32-DONTCALL,11,Goto(s|11) exten => sw-32-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-32-TORTURE,11,Goto(s|11) [macro-std-priv-exten_31] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-33-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_31-33) exten => _sw-33-.,10,Voicemail(u${ext}) exten => _sw-33-.,11,Goto(s|11) exten => sw-33-,10,Goto(sw-33-.|10) exten => sw-33-NOANSWER,10,Voicemail(u${ext}) exten => sw-33-NOANSWER,11,Goto(s|11) exten => sw-33-ANSWER,10,Goto(s|11) exten => sw-33-BUSY,10,Voicemail(b${ext}) exten => sw-33-BUSY,11,Goto(s|11) exten => sw-33-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-33-DONTCALL,11,Goto(s|11) exten => sw-33-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-33-TORTURE,11,Goto(s|11) [macro-std-priv-exten_32] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-34-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_32-34) exten => _sw-34-.,10,Voicemail(u${ext}) exten => _sw-34-.,11,Goto(s|11) exten => sw-34-,10,Goto(sw-34-.|10) exten => sw-34-NOANSWER,10,Voicemail(u${ext}) exten => sw-34-NOANSWER,11,Goto(s|11) exten => sw-34-ANSWER,10,Goto(s|11) exten => sw-34-BUSY,10,Voicemail(b${ext}) exten => sw-34-BUSY,11,Goto(s|11) exten => sw-34-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-34-DONTCALL,11,Goto(s|11) exten => sw-34-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-34-TORTURE,11,Goto(s|11) [macro-std-priv-exten_33] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-35-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_33-35) exten => _sw-35-.,10,Voicemail(u${ext}) exten => _sw-35-.,11,Goto(s|11) exten => sw-35-,10,Goto(sw-35-.|10) exten => sw-35-NOANSWER,10,Voicemail(u${ext}) exten => sw-35-NOANSWER,11,Goto(s|11) exten => sw-35-ANSWER,10,Goto(s|11) exten => sw-35-BUSY,10,Voicemail(b${ext}) exten => sw-35-BUSY,11,Goto(s|11) exten => sw-35-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-35-DONTCALL,11,Goto(s|11) exten => sw-35-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-35-TORTURE,11,Goto(s|11) [macro-std-priv-exten_34] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-36-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_34-36) exten => _sw-36-.,10,Voicemail(u${ext}) exten => _sw-36-.,11,Goto(s|11) exten => sw-36-,10,Goto(sw-36-.|10) exten => sw-36-NOANSWER,10,Voicemail(u${ext}) exten => sw-36-NOANSWER,11,Goto(s|11) exten => sw-36-ANSWER,10,Goto(s|11) exten => sw-36-BUSY,10,Voicemail(b${ext}) exten => sw-36-BUSY,11,Goto(s|11) exten => sw-36-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-36-DONTCALL,11,Goto(s|11) exten => sw-36-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-36-TORTURE,11,Goto(s|11) [macro-std-priv-exten_35] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-37-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_35-37) exten => _sw-37-.,10,Voicemail(u${ext}) exten => _sw-37-.,11,Goto(s|11) exten => sw-37-,10,Goto(sw-37-.|10) exten => sw-37-NOANSWER,10,Voicemail(u${ext}) exten => sw-37-NOANSWER,11,Goto(s|11) exten => sw-37-ANSWER,10,Goto(s|11) exten => sw-37-BUSY,10,Voicemail(b${ext}) exten => sw-37-BUSY,11,Goto(s|11) exten => sw-37-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-37-DONTCALL,11,Goto(s|11) exten => sw-37-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-37-TORTURE,11,Goto(s|11) [macro-std-priv-exten_36] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-38-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_36-38) exten => _sw-38-.,10,Voicemail(u${ext}) exten => _sw-38-.,11,Goto(s|11) exten => sw-38-,10,Goto(sw-38-.|10) exten => sw-38-NOANSWER,10,Voicemail(u${ext}) exten => sw-38-NOANSWER,11,Goto(s|11) exten => sw-38-ANSWER,10,Goto(s|11) exten => sw-38-BUSY,10,Voicemail(b${ext}) exten => sw-38-BUSY,11,Goto(s|11) exten => sw-38-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-38-DONTCALL,11,Goto(s|11) exten => sw-38-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-38-TORTURE,11,Goto(s|11) [macro-std-priv-exten_37] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-39-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_37-39) exten => _sw-39-.,10,Voicemail(u${ext}) exten => _sw-39-.,11,Goto(s|11) exten => sw-39-,10,Goto(sw-39-.|10) exten => sw-39-NOANSWER,10,Voicemail(u${ext}) exten => sw-39-NOANSWER,11,Goto(s|11) exten => sw-39-ANSWER,10,Goto(s|11) exten => sw-39-BUSY,10,Voicemail(b${ext}) exten => sw-39-BUSY,11,Goto(s|11) exten => sw-39-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-39-DONTCALL,11,Goto(s|11) exten => sw-39-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-39-TORTURE,11,Goto(s|11) [macro-std-priv-exten_38] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-40-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_38-40) exten => _sw-40-.,10,Voicemail(u${ext}) exten => _sw-40-.,11,Goto(s|11) exten => sw-40-,10,Goto(sw-40-.|10) exten => sw-40-NOANSWER,10,Voicemail(u${ext}) exten => sw-40-NOANSWER,11,Goto(s|11) exten => sw-40-ANSWER,10,Goto(s|11) exten => sw-40-BUSY,10,Voicemail(b${ext}) exten => sw-40-BUSY,11,Goto(s|11) exten => sw-40-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-40-DONTCALL,11,Goto(s|11) exten => sw-40-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-40-TORTURE,11,Goto(s|11) [macro-std-priv-exten_39] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-41-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_39-41) exten => _sw-41-.,10,Voicemail(u${ext}) exten => _sw-41-.,11,Goto(s|11) exten => sw-41-,10,Goto(sw-41-.|10) exten => sw-41-NOANSWER,10,Voicemail(u${ext}) exten => sw-41-NOANSWER,11,Goto(s|11) exten => sw-41-ANSWER,10,Goto(s|11) exten => sw-41-BUSY,10,Voicemail(b${ext}) exten => sw-41-BUSY,11,Goto(s|11) exten => sw-41-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-41-DONTCALL,11,Goto(s|11) exten => sw-41-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-41-TORTURE,11,Goto(s|11) [macro-std-priv-exten_40] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-42-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_40-42) exten => _sw-42-.,10,Voicemail(u${ext}) exten => _sw-42-.,11,Goto(s|11) exten => sw-42-,10,Goto(sw-42-.|10) exten => sw-42-NOANSWER,10,Voicemail(u${ext}) exten => sw-42-NOANSWER,11,Goto(s|11) exten => sw-42-ANSWER,10,Goto(s|11) exten => sw-42-BUSY,10,Voicemail(b${ext}) exten => sw-42-BUSY,11,Goto(s|11) exten => sw-42-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-42-DONTCALL,11,Goto(s|11) exten => sw-42-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-42-TORTURE,11,Goto(s|11) [macro-std-priv-exten_41] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-43-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_41-43) exten => _sw-43-.,10,Voicemail(u${ext}) exten => _sw-43-.,11,Goto(s|11) exten => sw-43-,10,Goto(sw-43-.|10) exten => sw-43-NOANSWER,10,Voicemail(u${ext}) exten => sw-43-NOANSWER,11,Goto(s|11) exten => sw-43-ANSWER,10,Goto(s|11) exten => sw-43-BUSY,10,Voicemail(b${ext}) exten => sw-43-BUSY,11,Goto(s|11) exten => sw-43-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-43-DONTCALL,11,Goto(s|11) exten => sw-43-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-43-TORTURE,11,Goto(s|11) [macro-std-priv-exten_42] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-44-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_42-44) exten => _sw-44-.,10,Voicemail(u${ext}) exten => _sw-44-.,11,Goto(s|11) exten => sw-44-,10,Goto(sw-44-.|10) exten => sw-44-NOANSWER,10,Voicemail(u${ext}) exten => sw-44-NOANSWER,11,Goto(s|11) exten => sw-44-ANSWER,10,Goto(s|11) exten => sw-44-BUSY,10,Voicemail(b${ext}) exten => sw-44-BUSY,11,Goto(s|11) exten => sw-44-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-44-DONTCALL,11,Goto(s|11) exten => sw-44-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-44-TORTURE,11,Goto(s|11) [macro-std-priv-exten_43] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-45-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_43-45) exten => _sw-45-.,10,Voicemail(u${ext}) exten => _sw-45-.,11,Goto(s|11) exten => sw-45-,10,Goto(sw-45-.|10) exten => sw-45-NOANSWER,10,Voicemail(u${ext}) exten => sw-45-NOANSWER,11,Goto(s|11) exten => sw-45-ANSWER,10,Goto(s|11) exten => sw-45-BUSY,10,Voicemail(b${ext}) exten => sw-45-BUSY,11,Goto(s|11) exten => sw-45-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-45-DONTCALL,11,Goto(s|11) exten => sw-45-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-45-TORTURE,11,Goto(s|11) [macro-std-priv-exten_44] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-46-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_44-46) exten => _sw-46-.,10,Voicemail(u${ext}) exten => _sw-46-.,11,Goto(s|11) exten => sw-46-,10,Goto(sw-46-.|10) exten => sw-46-NOANSWER,10,Voicemail(u${ext}) exten => sw-46-NOANSWER,11,Goto(s|11) exten => sw-46-ANSWER,10,Goto(s|11) exten => sw-46-BUSY,10,Voicemail(b${ext}) exten => sw-46-BUSY,11,Goto(s|11) exten => sw-46-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-46-DONTCALL,11,Goto(s|11) exten => sw-46-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-46-TORTURE,11,Goto(s|11) [macro-std-priv-exten_45] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-47-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_45-47) exten => _sw-47-.,10,Voicemail(u${ext}) exten => _sw-47-.,11,Goto(s|11) exten => sw-47-,10,Goto(sw-47-.|10) exten => sw-47-NOANSWER,10,Voicemail(u${ext}) exten => sw-47-NOANSWER,11,Goto(s|11) exten => sw-47-ANSWER,10,Goto(s|11) exten => sw-47-BUSY,10,Voicemail(b${ext}) exten => sw-47-BUSY,11,Goto(s|11) exten => sw-47-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-47-DONTCALL,11,Goto(s|11) exten => sw-47-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-47-TORTURE,11,Goto(s|11) [macro-std-priv-exten_46] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-48-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_46-48) exten => _sw-48-.,10,Voicemail(u${ext}) exten => _sw-48-.,11,Goto(s|11) exten => sw-48-,10,Goto(sw-48-.|10) exten => sw-48-NOANSWER,10,Voicemail(u${ext}) exten => sw-48-NOANSWER,11,Goto(s|11) exten => sw-48-ANSWER,10,Goto(s|11) exten => sw-48-BUSY,10,Voicemail(b${ext}) exten => sw-48-BUSY,11,Goto(s|11) exten => sw-48-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-48-DONTCALL,11,Goto(s|11) exten => sw-48-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-48-TORTURE,11,Goto(s|11) [macro-std-priv-exten_47] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-49-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_47-49) exten => _sw-49-.,10,Voicemail(u${ext}) exten => _sw-49-.,11,Goto(s|11) exten => sw-49-,10,Goto(sw-49-.|10) exten => sw-49-NOANSWER,10,Voicemail(u${ext}) exten => sw-49-NOANSWER,11,Goto(s|11) exten => sw-49-ANSWER,10,Goto(s|11) exten => sw-49-BUSY,10,Voicemail(b${ext}) exten => sw-49-BUSY,11,Goto(s|11) exten => sw-49-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-49-DONTCALL,11,Goto(s|11) exten => sw-49-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-49-TORTURE,11,Goto(s|11) [macro-std-priv-exten_48] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-50-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_48-50) exten => _sw-50-.,10,Voicemail(u${ext}) exten => _sw-50-.,11,Goto(s|11) exten => sw-50-,10,Goto(sw-50-.|10) exten => sw-50-NOANSWER,10,Voicemail(u${ext}) exten => sw-50-NOANSWER,11,Goto(s|11) exten => sw-50-ANSWER,10,Goto(s|11) exten => sw-50-BUSY,10,Voicemail(b${ext}) exten => sw-50-BUSY,11,Goto(s|11) exten => sw-50-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-50-DONTCALL,11,Goto(s|11) exten => sw-50-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-50-TORTURE,11,Goto(s|11) [macro-std-priv-exten_49] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-51-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_49-51) exten => _sw-51-.,10,Voicemail(u${ext}) exten => _sw-51-.,11,Goto(s|11) exten => sw-51-,10,Goto(sw-51-.|10) exten => sw-51-NOANSWER,10,Voicemail(u${ext}) exten => sw-51-NOANSWER,11,Goto(s|11) exten => sw-51-ANSWER,10,Goto(s|11) exten => sw-51-BUSY,10,Voicemail(b${ext}) exten => sw-51-BUSY,11,Goto(s|11) exten => sw-51-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-51-DONTCALL,11,Goto(s|11) exten => sw-51-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-51-TORTURE,11,Goto(s|11) [macro-std-priv-exten_50] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-52-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_50-52) exten => _sw-52-.,10,Voicemail(u${ext}) exten => _sw-52-.,11,Goto(s|11) exten => sw-52-,10,Goto(sw-52-.|10) exten => sw-52-NOANSWER,10,Voicemail(u${ext}) exten => sw-52-NOANSWER,11,Goto(s|11) exten => sw-52-ANSWER,10,Goto(s|11) exten => sw-52-BUSY,10,Voicemail(b${ext}) exten => sw-52-BUSY,11,Goto(s|11) exten => sw-52-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-52-DONTCALL,11,Goto(s|11) exten => sw-52-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-52-TORTURE,11,Goto(s|11) [macro-std-priv-exten_51] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-53-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_51-53) exten => _sw-53-.,10,Voicemail(u${ext}) exten => _sw-53-.,11,Goto(s|11) exten => sw-53-,10,Goto(sw-53-.|10) exten => sw-53-NOANSWER,10,Voicemail(u${ext}) exten => sw-53-NOANSWER,11,Goto(s|11) exten => sw-53-ANSWER,10,Goto(s|11) exten => sw-53-BUSY,10,Voicemail(b${ext}) exten => sw-53-BUSY,11,Goto(s|11) exten => sw-53-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-53-DONTCALL,11,Goto(s|11) exten => sw-53-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-53-TORTURE,11,Goto(s|11) [macro-std-priv-exten_52] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-54-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_52-54) exten => _sw-54-.,10,Voicemail(u${ext}) exten => _sw-54-.,11,Goto(s|11) exten => sw-54-,10,Goto(sw-54-.|10) exten => sw-54-NOANSWER,10,Voicemail(u${ext}) exten => sw-54-NOANSWER,11,Goto(s|11) exten => sw-54-ANSWER,10,Goto(s|11) exten => sw-54-BUSY,10,Voicemail(b${ext}) exten => sw-54-BUSY,11,Goto(s|11) exten => sw-54-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-54-DONTCALL,11,Goto(s|11) exten => sw-54-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-54-TORTURE,11,Goto(s|11) [macro-std-priv-exten_53] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-55-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_53-55) exten => _sw-55-.,10,Voicemail(u${ext}) exten => _sw-55-.,11,Goto(s|11) exten => sw-55-,10,Goto(sw-55-.|10) exten => sw-55-NOANSWER,10,Voicemail(u${ext}) exten => sw-55-NOANSWER,11,Goto(s|11) exten => sw-55-ANSWER,10,Goto(s|11) exten => sw-55-BUSY,10,Voicemail(b${ext}) exten => sw-55-BUSY,11,Goto(s|11) exten => sw-55-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-55-DONTCALL,11,Goto(s|11) exten => sw-55-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-55-TORTURE,11,Goto(s|11) [macro-std-priv-exten_54] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-56-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_54-56) exten => _sw-56-.,10,Voicemail(u${ext}) exten => _sw-56-.,11,Goto(s|11) exten => sw-56-,10,Goto(sw-56-.|10) exten => sw-56-NOANSWER,10,Voicemail(u${ext}) exten => sw-56-NOANSWER,11,Goto(s|11) exten => sw-56-ANSWER,10,Goto(s|11) exten => sw-56-BUSY,10,Voicemail(b${ext}) exten => sw-56-BUSY,11,Goto(s|11) exten => sw-56-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-56-DONTCALL,11,Goto(s|11) exten => sw-56-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-56-TORTURE,11,Goto(s|11) [macro-std-priv-exten_55] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-57-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_55-57) exten => _sw-57-.,10,Voicemail(u${ext}) exten => _sw-57-.,11,Goto(s|11) exten => sw-57-,10,Goto(sw-57-.|10) exten => sw-57-NOANSWER,10,Voicemail(u${ext}) exten => sw-57-NOANSWER,11,Goto(s|11) exten => sw-57-ANSWER,10,Goto(s|11) exten => sw-57-BUSY,10,Voicemail(b${ext}) exten => sw-57-BUSY,11,Goto(s|11) exten => sw-57-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-57-DONTCALL,11,Goto(s|11) exten => sw-57-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-57-TORTURE,11,Goto(s|11) [macro-std-priv-exten_56] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-58-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_56-58) exten => _sw-58-.,10,Voicemail(u${ext}) exten => _sw-58-.,11,Goto(s|11) exten => sw-58-,10,Goto(sw-58-.|10) exten => sw-58-NOANSWER,10,Voicemail(u${ext}) exten => sw-58-NOANSWER,11,Goto(s|11) exten => sw-58-ANSWER,10,Goto(s|11) exten => sw-58-BUSY,10,Voicemail(b${ext}) exten => sw-58-BUSY,11,Goto(s|11) exten => sw-58-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-58-DONTCALL,11,Goto(s|11) exten => sw-58-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-58-TORTURE,11,Goto(s|11) [macro-std-priv-exten_57] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-59-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_57-59) exten => _sw-59-.,10,Voicemail(u${ext}) exten => _sw-59-.,11,Goto(s|11) exten => sw-59-,10,Goto(sw-59-.|10) exten => sw-59-NOANSWER,10,Voicemail(u${ext}) exten => sw-59-NOANSWER,11,Goto(s|11) exten => sw-59-ANSWER,10,Goto(s|11) exten => sw-59-BUSY,10,Voicemail(b${ext}) exten => sw-59-BUSY,11,Goto(s|11) exten => sw-59-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-59-DONTCALL,11,Goto(s|11) exten => sw-59-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-59-TORTURE,11,Goto(s|11) [macro-std-priv-exten_58] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-60-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_58-60) exten => _sw-60-.,10,Voicemail(u${ext}) exten => _sw-60-.,11,Goto(s|11) exten => sw-60-,10,Goto(sw-60-.|10) exten => sw-60-NOANSWER,10,Voicemail(u${ext}) exten => sw-60-NOANSWER,11,Goto(s|11) exten => sw-60-ANSWER,10,Goto(s|11) exten => sw-60-BUSY,10,Voicemail(b${ext}) exten => sw-60-BUSY,11,Goto(s|11) exten => sw-60-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-60-DONTCALL,11,Goto(s|11) exten => sw-60-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-60-TORTURE,11,Goto(s|11) [macro-std-priv-exten_59] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-61-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_59-61) exten => _sw-61-.,10,Voicemail(u${ext}) exten => _sw-61-.,11,Goto(s|11) exten => sw-61-,10,Goto(sw-61-.|10) exten => sw-61-NOANSWER,10,Voicemail(u${ext}) exten => sw-61-NOANSWER,11,Goto(s|11) exten => sw-61-ANSWER,10,Goto(s|11) exten => sw-61-BUSY,10,Voicemail(b${ext}) exten => sw-61-BUSY,11,Goto(s|11) exten => sw-61-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-61-DONTCALL,11,Goto(s|11) exten => sw-61-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-61-TORTURE,11,Goto(s|11) [macro-std-priv-exten_60] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-62-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_60-62) exten => _sw-62-.,10,Voicemail(u${ext}) exten => _sw-62-.,11,Goto(s|11) exten => sw-62-,10,Goto(sw-62-.|10) exten => sw-62-NOANSWER,10,Voicemail(u${ext}) exten => sw-62-NOANSWER,11,Goto(s|11) exten => sw-62-ANSWER,10,Goto(s|11) exten => sw-62-BUSY,10,Voicemail(b${ext}) exten => sw-62-BUSY,11,Goto(s|11) exten => sw-62-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-62-DONTCALL,11,Goto(s|11) exten => sw-62-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-62-TORTURE,11,Goto(s|11) [macro-std-priv-exten_61] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-63-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_61-63) exten => _sw-63-.,10,Voicemail(u${ext}) exten => _sw-63-.,11,Goto(s|11) exten => sw-63-,10,Goto(sw-63-.|10) exten => sw-63-NOANSWER,10,Voicemail(u${ext}) exten => sw-63-NOANSWER,11,Goto(s|11) exten => sw-63-ANSWER,10,Goto(s|11) exten => sw-63-BUSY,10,Voicemail(b${ext}) exten => sw-63-BUSY,11,Goto(s|11) exten => sw-63-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-63-DONTCALL,11,Goto(s|11) exten => sw-63-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-63-TORTURE,11,Goto(s|11) [macro-std-priv-exten_62] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-64-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_62-64) exten => _sw-64-.,10,Voicemail(u${ext}) exten => _sw-64-.,11,Goto(s|11) exten => sw-64-,10,Goto(sw-64-.|10) exten => sw-64-NOANSWER,10,Voicemail(u${ext}) exten => sw-64-NOANSWER,11,Goto(s|11) exten => sw-64-ANSWER,10,Goto(s|11) exten => sw-64-BUSY,10,Voicemail(b${ext}) exten => sw-64-BUSY,11,Goto(s|11) exten => sw-64-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-64-DONTCALL,11,Goto(s|11) exten => sw-64-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-64-TORTURE,11,Goto(s|11) [macro-std-priv-exten_63] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-65-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_63-65) exten => _sw-65-.,10,Voicemail(u${ext}) exten => _sw-65-.,11,Goto(s|11) exten => sw-65-,10,Goto(sw-65-.|10) exten => sw-65-NOANSWER,10,Voicemail(u${ext}) exten => sw-65-NOANSWER,11,Goto(s|11) exten => sw-65-ANSWER,10,Goto(s|11) exten => sw-65-BUSY,10,Voicemail(b${ext}) exten => sw-65-BUSY,11,Goto(s|11) exten => sw-65-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-65-DONTCALL,11,Goto(s|11) exten => sw-65-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-65-TORTURE,11,Goto(s|11) [macro-std-priv-exten_64] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-66-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_64-66) exten => _sw-66-.,10,Voicemail(u${ext}) exten => _sw-66-.,11,Goto(s|11) exten => sw-66-,10,Goto(sw-66-.|10) exten => sw-66-NOANSWER,10,Voicemail(u${ext}) exten => sw-66-NOANSWER,11,Goto(s|11) exten => sw-66-ANSWER,10,Goto(s|11) exten => sw-66-BUSY,10,Voicemail(b${ext}) exten => sw-66-BUSY,11,Goto(s|11) exten => sw-66-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-66-DONTCALL,11,Goto(s|11) exten => sw-66-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-66-TORTURE,11,Goto(s|11) [macro-std-priv-exten_65] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-67-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_65-67) exten => _sw-67-.,10,Voicemail(u${ext}) exten => _sw-67-.,11,Goto(s|11) exten => sw-67-,10,Goto(sw-67-.|10) exten => sw-67-NOANSWER,10,Voicemail(u${ext}) exten => sw-67-NOANSWER,11,Goto(s|11) exten => sw-67-ANSWER,10,Goto(s|11) exten => sw-67-BUSY,10,Voicemail(b${ext}) exten => sw-67-BUSY,11,Goto(s|11) exten => sw-67-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-67-DONTCALL,11,Goto(s|11) exten => sw-67-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-67-TORTURE,11,Goto(s|11) [macro-std-priv-exten_66] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-68-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_66-68) exten => _sw-68-.,10,Voicemail(u${ext}) exten => _sw-68-.,11,Goto(s|11) exten => sw-68-,10,Goto(sw-68-.|10) exten => sw-68-NOANSWER,10,Voicemail(u${ext}) exten => sw-68-NOANSWER,11,Goto(s|11) exten => sw-68-ANSWER,10,Goto(s|11) exten => sw-68-BUSY,10,Voicemail(b${ext}) exten => sw-68-BUSY,11,Goto(s|11) exten => sw-68-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-68-DONTCALL,11,Goto(s|11) exten => sw-68-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-68-TORTURE,11,Goto(s|11) [macro-std-priv-exten_67] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-69-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_67-69) exten => _sw-69-.,10,Voicemail(u${ext}) exten => _sw-69-.,11,Goto(s|11) exten => sw-69-,10,Goto(sw-69-.|10) exten => sw-69-NOANSWER,10,Voicemail(u${ext}) exten => sw-69-NOANSWER,11,Goto(s|11) exten => sw-69-ANSWER,10,Goto(s|11) exten => sw-69-BUSY,10,Voicemail(b${ext}) exten => sw-69-BUSY,11,Goto(s|11) exten => sw-69-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-69-DONTCALL,11,Goto(s|11) exten => sw-69-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-69-TORTURE,11,Goto(s|11) [macro-std-priv-exten_68] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-70-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_68-70) exten => _sw-70-.,10,Voicemail(u${ext}) exten => _sw-70-.,11,Goto(s|11) exten => sw-70-,10,Goto(sw-70-.|10) exten => sw-70-NOANSWER,10,Voicemail(u${ext}) exten => sw-70-NOANSWER,11,Goto(s|11) exten => sw-70-ANSWER,10,Goto(s|11) exten => sw-70-BUSY,10,Voicemail(b${ext}) exten => sw-70-BUSY,11,Goto(s|11) exten => sw-70-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-70-DONTCALL,11,Goto(s|11) exten => sw-70-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-70-TORTURE,11,Goto(s|11) [macro-std-priv-exten_69] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-71-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_69-71) exten => _sw-71-.,10,Voicemail(u${ext}) exten => _sw-71-.,11,Goto(s|11) exten => sw-71-,10,Goto(sw-71-.|10) exten => sw-71-NOANSWER,10,Voicemail(u${ext}) exten => sw-71-NOANSWER,11,Goto(s|11) exten => sw-71-ANSWER,10,Goto(s|11) exten => sw-71-BUSY,10,Voicemail(b${ext}) exten => sw-71-BUSY,11,Goto(s|11) exten => sw-71-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-71-DONTCALL,11,Goto(s|11) exten => sw-71-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-71-TORTURE,11,Goto(s|11) [macro-std-priv-exten_70] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-72-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_70-72) exten => _sw-72-.,10,Voicemail(u${ext}) exten => _sw-72-.,11,Goto(s|11) exten => sw-72-,10,Goto(sw-72-.|10) exten => sw-72-NOANSWER,10,Voicemail(u${ext}) exten => sw-72-NOANSWER,11,Goto(s|11) exten => sw-72-ANSWER,10,Goto(s|11) exten => sw-72-BUSY,10,Voicemail(b${ext}) exten => sw-72-BUSY,11,Goto(s|11) exten => sw-72-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-72-DONTCALL,11,Goto(s|11) exten => sw-72-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-72-TORTURE,11,Goto(s|11) [macro-std-priv-exten_71] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-73-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_71-73) exten => _sw-73-.,10,Voicemail(u${ext}) exten => _sw-73-.,11,Goto(s|11) exten => sw-73-,10,Goto(sw-73-.|10) exten => sw-73-NOANSWER,10,Voicemail(u${ext}) exten => sw-73-NOANSWER,11,Goto(s|11) exten => sw-73-ANSWER,10,Goto(s|11) exten => sw-73-BUSY,10,Voicemail(b${ext}) exten => sw-73-BUSY,11,Goto(s|11) exten => sw-73-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-73-DONTCALL,11,Goto(s|11) exten => sw-73-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-73-TORTURE,11,Goto(s|11) [macro-std-priv-exten_72] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-74-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_72-74) exten => _sw-74-.,10,Voicemail(u${ext}) exten => _sw-74-.,11,Goto(s|11) exten => sw-74-,10,Goto(sw-74-.|10) exten => sw-74-NOANSWER,10,Voicemail(u${ext}) exten => sw-74-NOANSWER,11,Goto(s|11) exten => sw-74-ANSWER,10,Goto(s|11) exten => sw-74-BUSY,10,Voicemail(b${ext}) exten => sw-74-BUSY,11,Goto(s|11) exten => sw-74-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-74-DONTCALL,11,Goto(s|11) exten => sw-74-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-74-TORTURE,11,Goto(s|11) [macro-std-priv-exten_73] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-75-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten_73-75) exten => _sw-75-.,10,Voicemail(u${ext}) exten => _sw-75-.,11,Goto(s|11) exten => sw-75-,10,Goto(sw-75-.|10) exten => sw-75-NOANSWER,10,Voicemail(u${ext}) exten => sw-75-NOANSWER,11,Goto(s|11) exten => sw-75-ANSWER,10,Goto(s|11) exten => sw-75-BUSY,10,Voicemail(b${ext}) exten => sw-75-BUSY,11,Goto(s|11) exten => sw-75-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-75-DONTCALL,11,Goto(s|11) exten => sw-75-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-75-TORTURE,11,Goto(s|11) [macro-std-priv-exten] exten => s,1,Set(dev=${ARG1}) exten => s,2,Set(ext=${ARG2}) exten => s,3,Set(timeout=${ARG3}) exten => s,4,Set(opts=${ARG4}) exten => s,5,Set(torcont=${ARG5}) exten => s,6,Set(dontcont=${ARG6}) exten => s,7,Dial(${dev}|${timeout}|${opts}) exten => s,8,NoOp(${DIALSTATUS} was chosen) exten => s,9,Set(~~EXTEN~~=${EXTEN}) exten => s,10,Goto(sw-76-${DIALSTATUS}|10) exten => s,11,NoOp(Finish switch-std-priv-exten-76) exten => _sw-76-.,10,Voicemail(u${ext}) exten => _sw-76-.,11,Goto(s|11) exten => sw-76-,10,Goto(sw-76-.|10) exten => sw-76-NOANSWER,10,Voicemail(u${ext}) exten => sw-76-NOANSWER,11,Goto(s|11) exten => sw-76-ANSWER,10,Goto(s|11) exten => sw-76-BUSY,10,Voicemail(b${ext}) exten => sw-76-BUSY,11,Goto(s|11) exten => sw-76-DONTCALL,10,Goto(${dontcont}|s|begin) exten => sw-76-DONTCALL,11,Goto(s|11) exten => sw-76-TORTURE,10,Goto(${torcont}|s|begin) exten => sw-76-TORTURE,11,Goto(s|11) [macro-fillcidname] exten => s,1,GotoIf($["${CALLERID(number)}" = "" ]?2:3) exten => s,2,Goto(13) exten => s,3,NoOp(Finish if-fillcidname-77) exten => s,4,Set(cidn=${DB(cidname/${CALLERID(num)})}) exten => s,5,GotoIf($["${CALLERID(name)}" != "" ]?6:9) exten => s,6,GotoIf($[("${cidn}" = "Privacy Manager" & "${CALLERID(name)}" != "Privacy Manager") | "${cidn}" = "" ]?7:8) exten => s,7,Set(DB(cidname/${CALLERID(number)})=${CALLERID(name)}) exten => s,8,NoOp(Finish if-if-fillcidname-78-79) exten => s,9,NoOp(Finish if-fillcidname-78) exten => s,10,GotoIf($[( "${cidn}" != "" ) & ( "${CALLERID(name)}" = "" | "${CALLERID(name)}" = "CODY\,WY " | "${CALLERID(name)}" = "POWELL\,WY " | "${CALLERID(name)}" = "WIRELESS CALLER" | "${CALLERID(name)}" = "SUBSCRIBER\,WIRE" | "${CALLERID(name)}" = "CELLULAR ONE" | "${CALLERID(name)}" = "Cellular One Customer" | "${CALLERID(name)}" = "CELLULAR ONE " | "${CALLERID(name)}" = "Privacy Manager" | "${CALLERID(name)}" = "RIVERTON\,WY " | "${CALLERID(name)}" = "BASIN\,WY " | "${CALLERID(name)}" = "BILLINGS\,MT " | "${CALLERID(name)}" = "PROVO\,UT " | "${CALLERID(name)}" = "TOLL FREE " ) ]?11:12) exten => s,11,Set(CALLERID(name)=${cidn}) exten => s,12,NoOp(Finish if-fillcidname-80) exten => s,13,NoOp(End of Macro fillcidname-s) [macro-ciddial] exten => s,1,Set(dialnum=${ARG1}) exten => s,2,Set(lookup=${ARG2}) exten => s,3,Set(waittime=${ARG3}) exten => s,4,Set(dialopts=${ARG4}) exten => s,5,Set(ddev=${ARG5}) exten => s,6,Set(cidnu=${CALLERID(num)}) exten => s,7,Set(cidn=${DB(cidname/${lookup})}) exten => s,8,Set(CALLERID(name)=${cidn}) exten => s,9,Dial(${ddev}/${dialnum}|${waittime}|${dialopts}) exten => s,10,GotoIf($["${DIALSTATUS}" = "CHANUNAVAIL" ]?11:19) exten => s,11,BackGround(try_voip) exten => s,12,Set(CALLERID(num)=$[7075679201]) exten => s,13,Dial(SIP/1${lookup}@tctwest|${waittime}|${dialopts}) exten => s,14,GotoIf($["${DIALSTATUS}" = "CHANUNAVAIL" ]?15:18) exten => s,15,BackGround(try_cell) exten => s,16,Set(CALLERID(num)=$[${cidnu}]) exten => s,17,Dial(Zap/2/${lookup}|${waittime}|${dialopts}) exten => s,18,NoOp(Finish if-if-ciddial-81-82) exten => s,19,NoOp(Finish if-ciddial-81) [macro-ciddial3] exten => s,1,Set(dialnum=${ARG1}) exten => s,2,Set(lookup=${ARG2}) exten => s,3,Set(waittime=${ARG3}) exten => s,4,Set(dialopts=${ARG4}) exten => s,5,Set(ddev=${ARG5}) exten => s,6,Set(cidnu=${CALLERID(num)}) exten => s,7,Set(cidn=${DB(cidname/${lookup})}) exten => s,8,Set(CALLERID(name)=${cidn}) exten => s,9,Dial(${ddev}/${dialnum}|${waittime}|${dialopts}) exten => s,10,GotoIf($["${DIALSTATUS}" = "CHANUNAVAIL" ]?11:13) exten => s,11,BackGround(try_cell) exten => s,12,Dial(Zap/2/${lookup}|${waittime}|${dialopts}) exten => s,13,NoOp(Finish if-ciddial3-83) [macro-ciddial2] exten => s,1,Set(dialnum=${ARG1}) exten => s,2,Set(lookup=${ARG2}) exten => s,3,Set(waittime=${ARG3}) exten => s,4,Set(dialopts=${ARG4}) exten => s,5,Set(ddev=${ARG5}) exten => s,6,Set(cidn=${DB(cidname/${lookup})}) exten => s,7,Set(cidnu=${CALLERID(num)}) exten => s,8,Set(CALLERID(name)=${cidn}) exten => s,9,Set(CALLERID(num)=7075679201) exten => s,10,Dial(SIP/1${lookup}@tctwest|${waittime}|${dialopts}) exten => s,11,GotoIf($["${DIALSTATUS}" = "CHANUNAVAIL" ]?12:19) exten => s,12,Set(CALLERID(num)=${cidnu}) exten => s,13,BackGround(try_zap) exten => s,14,Dial(${ddev}/${dialnum}|${waittime}|${dialopts}) exten => s,15,GotoIf($["${DIALSTATUS}" = "CHANUNAVAIL" ]?16:18) exten => s,16,BackGround(try_cell) exten => s,17,Dial(Zap/2/${lookup}|${waittime}|${dialopts}) exten => s,18,NoOp(Finish if-if-ciddial2-84-85) exten => s,19,NoOp(Finish if-ciddial2-84) [macro-callerid-liar] exten => s,1,TrySystem(/usr/bin/play /var/lib/asterisk/sounds/priv-callerintros/LIAR.gsm&) exten => s,2,Background(priv-liar) exten => s,3,Hangup() [macro-callerid-bad] exten => s,1,Set(mycid=$[${CALLERID(num)}:"1([0-9]+)"]) exten => s,2,Set(CALLERID(num)=${mycid}) exten => s,3,Wait(0) [privacyManagerFailed] exten => s,1(begin),Background(PrivManInstructions) exten => s,2,PrivacyManager() exten => s,3,GotoIf($["${PRIVACYMGRSTATUS}" = "FAILED" ]?4:11) exten => s,4,Background(tt-allbusy) exten => s,5,Background(tt-somethingwrong) exten => s,6,Background(tt-monkeysintro) exten => s,7,Background(tt-monkeys) exten => s,8,Background(tt-weasels) exten => s,9,Hangup() exten => s,10,Goto(12) exten => s,11,Goto(homeline|s|postPriv) exten => s,12,NoOp(Finish if-privacyManagerFailed-86) [homeline] exten => s,1(begin),Answer() exten => s,2,Set(repeatcount=0) exten => s,3,Zapateller(nocallerid) exten => s,4,PrivacyManager() exten => s,5,GotoIf($["${PRIVACYMGRSTATUS}" = "FAILED" ]?6:10) exten => s,6,TrySystem(/usr/bin/play /var/lib/asterisk/sounds/privmanfailed.gsm) exten => s,7,Macro(std-priv-exten|Zap/3r1&Zap/5r1|2|25|mtw|telemarket|telemarket) exten => s,8,Hangup() exten => s,9,Goto(105) exten => s,10,NoOp(Finish if-homeline-87) exten => s,11(postPriv),Macro(fillcidname) exten => s,12,Set(CONFCIDNA=${CALLERID(name)}) exten => s,13,Set(CONFCIDNU=${CALLERID(num)}) exten => s,14,AGI(callall) exten => s,15,AGI(submit-announce.agi) exten => s,16,GotoIf($["${CALLERID(num)}" : "1" ]?17:18) exten => s,17,Macro(callerid-bad) exten => s,18,NoOp(Finish if-homeline-88) exten => s,19,GotoIf($["${CALLERID(num)}" = "7077577685" & "${CALLERID(name)}" : "Privacy Manager" ]?20:21) exten => s,20,Macro(callerid-liar) exten => s,21,NoOp(Finish if-homeline-89) exten => s,22,TrySystem(/usr/local/bin/who-is-it ${CALLERID(num)} "${CALLERID(name)}"&) exten => s,23,Set(lds=${DB(playlds/${CALLERID(num)})}) exten => s,24,GotoIf($["${lds}" = "1" ]?25:26) exten => s,25,SetMusicOnHold(mohlds) exten => s,26,NoOp(Finish if-homeline-90) exten => s,27,Set(direct=$[${DB(DirectCall/${CALLERID(num)})}]) exten => s,28,GotoIf($["${direct}" != "" & ${direct} != 0 ]?29:37) exten => s,29,verbose(direct is XXX#${direct}XXXX) exten => s,30,Playback(greetings/direct) exten => s,31,Playback(/var/spool/asterisk/voicemail/default/${direct}/greet) exten => s,32,TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm) exten => s,33,TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/${direct}/greet.wav&) exten => s,34,Set(~~EXTEN~~=${EXTEN}) exten => s,35,Goto(sw-92-${direct}|10) exten => s,36,NoOp(Finish switch-if-homeline-91-92) exten => s,37,NoOp(Finish if-homeline-91) exten => s,38(loopback),GotoIfTime(*|*|20-25|dec?40) exten => s,39,Goto(42) exten => s,40,Playback(greetings/christmas) exten => s,41,Goto(103) exten => s,42,GotoIfTime(*|*|31|dec?44) exten => s,43,Goto(46) exten => s,44,Playback(greetings/newyear) exten => s,45,Goto(102) exten => s,46,GotoIfTime(*|*|1|jan?48) exten => s,47,Goto(50) exten => s,48,Playback(greetings/newyear) exten => s,49,Goto(101) exten => s,50,GotoIfTime(*|*|14|feb?52) exten => s,51,Goto(54) exten => s,52,Playback(greetings/valentines) exten => s,53,Goto(100) exten => s,54,GotoIfTime(*|*|17|mar?56) exten => s,55,Goto(58) exten => s,56,Playback(greetings/stPat) exten => s,57,Goto(99) exten => s,58,GotoIfTime(*|*|31|oct?60) exten => s,59,Goto(62) exten => s,60,Playback(greetings/halloween) exten => s,61,Goto(98) exten => s,62,GotoIfTime(*|mon|15-21|jan?64) exten => s,63,Goto(66) exten => s,64,Playback(greetings/mlkDay) exten => s,65,Goto(97) exten => s,66,GotoIfTime(*|thu|22-28|nov?68) exten => s,67,Goto(70) exten => s,68,Playback(greetings/thanksgiving) exten => s,69,Goto(96) exten => s,70,GotoIfTime(*|mon|25-31|may?72) exten => s,71,Goto(74) exten => s,72,Playback(greetings/memorial) exten => s,73,Goto(95) exten => s,74,GotoIfTime(*|mon|1-7|sep?76) exten => s,75,Goto(78) exten => s,76,Playback(greetings/labor) exten => s,77,Goto(94) exten => s,78,GotoIfTime(*|mon|15-21|feb?80) exten => s,79,Goto(82) exten => s,80,Playback(greetings/president) exten => s,81,Goto(93) exten => s,82,GotoIfTime(*|sun|8-14|may?84) exten => s,83,Goto(86) exten => s,84,Playback(greetings/mothers) exten => s,85,Goto(92) exten => s,86,GotoIfTime(*|sun|15-21|jun?88) exten => s,87,Goto(90) exten => s,88,Playback(greetings/fathers) exten => s,89,Goto(91) exten => s,90,Playback(greetings/hello) exten => s,91,NoOp(Finish iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-homeline-93-94-95-96-97-98-99-100-101-102-103-104-105) exten => s,92,NoOp(Finish iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-homeline-93-94-95-96-97-98-99-100-101-102-103-104) exten => s,93,NoOp(Finish iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-homeline-93-94-95-96-97-98-99-100-101-102-103) exten => s,94,NoOp(Finish iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-homeline-93-94-95-96-97-98-99-100-101-102) exten => s,95,NoOp(Finish iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-homeline-93-94-95-96-97-98-99-100-101) exten => s,96,NoOp(Finish iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-homeline-93-94-95-96-97-98-99-100) exten => s,97,NoOp(Finish iftime-iftime-iftime-iftime-iftime-iftime-iftime-homeline-93-94-95-96-97-98-99) exten => s,98,NoOp(Finish iftime-iftime-iftime-iftime-iftime-iftime-homeline-93-94-95-96-97-98) exten => s,99,NoOp(Finish iftime-iftime-iftime-iftime-iftime-homeline-93-94-95-96-97) exten => s,100,NoOp(Finish iftime-iftime-iftime-iftime-homeline-93-94-95-96) exten => s,101,NoOp(Finish iftime-iftime-iftime-homeline-93-94-95) exten => s,102,NoOp(Finish iftime-iftime-homeline-93-94) exten => s,103,NoOp(Finish iftime-homeline-93) exten => s,104,Background(murphy-homeline-intro1) exten => s,105,NoOp(End of Extension s) exten => _sw-92-.,10,Set(z=${direct}-2) exten => _sw-92-.,11,Goto(homeline-kids|${z}|1) exten => sw-92-,10,Goto(sw-92-.|10) exten => sw-92-2,10,Macro(std-priv-exten|Zap/3r1&Zap/5r1|2|25|mtw|telemarket|telemarket) exten => sw-92-2,11,Goto(s|loopback) exten => sw-92-1,10,Macro(std-priv-exten|Zap/6r3&Sip/murf|1|25|mpA(beep)tw|telemarket|telemarket) exten => sw-92-1,11,Goto(s|loopback) exten => 1,1,TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm) exten => 1,2,TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/2/greet.wav&) exten => 1,3,Macro(std-priv-exten|Zap/3r1&Zap/5r1|2|25|mtw|telemarket|telemarket) exten => 1,4,Goto(s|loopback) exten => 2,1,Goto(homeline-kids|s|begin) exten => 21,1,Dial(IAX2/seaniax|20|T) exten => 3,1,Macro(std-priv-exten|Zap/6r3&Sip/murf|1|25|mpA(beep)tw|telemarket|telemarket) exten => 3,2,Goto(s|loopback) exten => 4,1,VoicemailMain() exten => 4,2,Goto(s|loopback) exten => 5,1,Goto(home-introduction|s|begin) exten => 6,1,Goto(telemarket|s|begin) exten => 7,1,agi(tts-riddle.agi) exten => 7,2,Background(gsm/what-time-it-is2) exten => 7,3,SayUnixTime() exten => 7,4,Goto(s|loopback) exten => 792,1,Goto(pageall|s|begin) exten => 793,1,Read(zz||0||1|0) exten => 793,2,SayDigits(${zz}) exten => t,1,Set(repeatcount=${repeatcount} + 1) exten => t,2,GotoIf($[${repeatcount} < 3 ]?3:4) exten => t,3,Goto(s|loopback) exten => t,4,NoOp(Finish if-homeline-106) exten => t,5,Hangup() exten => i,1,Background(invalid) exten => i,2,Goto(s|loopback) exten => o,1,Congestion() exten => fax,1,Dial(Zap/4) [pageall] exten => s,1(begin),AGI(callall) exten => s,2,MeetMe(5555|dtqp) exten => s,3,MeetMeAdmin(5555|K) exten => s,4,Hangup() exten => h,1(begin),MeetMeAdmin(5555|K) exten => h,2,Background(conf-muted) exten => h,3,Hangup() [add-to-conference] exten => start,1,NoCDR() exten => start,2,MeetMe(5555|dmqp) exten => h,1,Hangup() [home-introduction] exten => s,1(begin),Background(intro-options) exten => 1,1,Playback(priv-callerintros/${CALLERID(num)}) exten => 1,2,Goto(s|begin) exten => 2,1,Goto(home-introduction-record|s|begin) exten => 3,1,Goto(homeline|s|loopback) exten => 4,1,Playback(intro-intro) exten => 4,2,Goto(s|begin) exten => t,1,Goto(s|begin) exten => i,1,Background(invalid) exten => i,2,Goto(s|begin) exten => o,1,Goto(s|begin) [home-introduction-record] exten => s,1(begin),Background(intro-record-choices) exten => 1,1,Playback(intro-record) exten => 1,2,Goto(2|begin) exten => 2,1(begin),Background(intro-start) exten => 2,2,Background(beep) exten => 2,3,Record(priv-callerintros/${CALLERID(num)}:gsm|3) exten => 2,4,Background(priv-callerintros/${CALLERID(num)}) exten => 2,5,Goto(home-introduction|s|begin) exten => t,1,Goto(s|begin) exten => i,1,Background(invalid) exten => i,2,Goto(s|begin) exten => o,1,Goto(s|begin) [homeline-kids] exten => s,1(begin),Background(murphy-homeline-kids) exten => 1,1,TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm) exten => 1,2,TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/3/greet.wav&) exten => 1,3,Macro(std-priv-exten|IAX2/seaniax&Zap/5r2|3|35|mtw|telemarket|telemarket) exten => 1,4,Goto(homeline|s|loopback) exten => 2,1,TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm) exten => 2,2,TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/4/greet.wav&) exten => 2,3,Voicemail(u4) exten => 2,4,Goto(homeline|s|loopback) exten => 3,1,TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm) exten => 3,2,TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/5/greet.wav&) exten => 3,3,Macro(std-priv-exten|Zap/3r2&Zap/5r2|5|35|mtw|telemarket|telemarket) exten => 3,4,Goto(homeline|s|loopback) exten => 4,1,TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm) exten => 4,2,TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/6/greet.wav&) exten => 4,3,Macro(std-priv-exten|Zap/3r2&Zap/5r2|6|35|mtw|telemarket|telemarket) exten => 4,4,Goto(homeline|s|loopback) exten => 5,1,TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm) exten => 5,2,TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/7/greet.wav&) exten => 5,3,Macro(std-priv-exten|Zap/3r2&Zap/5r2|7|35|mtw|telemarket|telemarket) exten => 5,4,Goto(homeline|s|loopback) exten => 6,1,TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm) exten => 6,2,TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/8/greet.wav&) exten => 6,3,Macro(std-priv-exten|Zap/3r2&Zap/5r2|8|35|mtw|telemarket|telemarket) exten => 6,4,Goto(homeline|s|loopback) exten => 7,1,TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm) exten => 7,2,TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/9/greet.wav&) exten => 7,3,Macro(std-priv-exten|Zap/3r2&Zap/5r2|9|35|mtw|telemarket|telemarket) exten => 7,4,Goto(homeline|s|loopback) exten => t,1,Goto(s|begin) exten => i,1,Background(invalid) exten => i,2,Goto(s|begin) exten => o,1,Goto(s|begin) [voipworkline] exten => s,1(begin),Answer() exten => s,2,TrySystem(/usr/local/bin/who-is-it ${CALLERID(num)} "${CALLERID(name)}"&) exten => s,3,Goto(workline|s|loopback) exten => 7075679201,1,Answer() exten => 7075679201,2,TrySystem(/usr/local/bin/who-is-it ${CALLERID(num)} "${CALLERID(name)}"&) exten => 7075679201,3,Goto(workline|s|loopback) [workline] exten => s,1(begin),Answer() exten => s,2,Wait(1) exten => s,3,Set(repeatcount=0) exten => s,4,Zapateller(nocallerid) exten => s,5,Macro(fillcidname) exten => s,6,TrySystem(/usr/local/bin/who-is-it ${CALLERID(num)} "${CALLERID(name)}"&) exten => s,7(loopback),Background(greetings/greeting) exten => s,8,Background(murphy-office-intro1) exten => 1,1,TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm) exten => 1,2,TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/1/greet.wav&) exten => 1,3,Macro(std-priv-exten|Zap/6&Sip/murf|1|30|mtw|telemarket|telemarket) exten => 1,4,Goto(s|loopback) exten => 4,1,VoicemailMain() exten => 4,2,Goto(s|loopback) exten => 6,1,Goto(telemarket|s|begin) exten => 793,1,Read(zz||0||1|0) exten => 793,2,SayDigits(${zz}) exten => t,1,Set(repeatcount=$[${repeatcount} + 1]) exten => t,2,GotoIf($[${repeatcount} < 3 ]?3:4) exten => t,3,Goto(s|loopback) exten => t,4,NoOp(Finish if-workline-107) exten => t,5,Hangup() exten => i,1,Background(invalid) exten => i,2,Goto(s|loopback) exten => o,1,Congestion() exten => fax,1,Answer() exten => fax,2,Dial(Zap/4) [dialFWD] ignorepat => 8 ignorepat => 9 exten => _83.,1,Set(CALLERID(name)=${FWDCIDNAME}) exten => _83.,2,Dial(IAX2/${FWDNUMBER}:${FWDPASSWORD}@iax2.fwdnet.net/${EXTEN:2}|60|r) exten => _83.,3,Congestion() exten => _82NXX,1,Set(CALLERID(name)=${FWDCIDNAME}) exten => _82NXX,2,Dial(IAX2/${FWDNUMBER}:${FWDPASSWORD}@iax2.fwdnet.net/${EXTEN:2}|60|r) exten => _82NXX,3,Congestion() exten => _92NXX,1,Set(CALLERID(name)=${FWDCIDNAME}) exten => _92NXX,2,Dial(IAX2/${FWDNUMBER}:${FWDPASSWORD}@iax2.fwdnet.net/${EXTEN:2}|60|r) exten => _92NXX,3,Congestion() [dialiaxtel] ignorepat => 8 ignorepat => 9 exten => _81700NXXXXXX,1,Dial(IAX2/zorch:zilchnoodle@iaxtel.com/${EXTEN:1}@iaxtel) exten => _81800NXXXXXX,1,Dial(IAX2/zorch:zilchnoodle@iaxtel.com/${EXTEN:1}@iaxtel) exten => _91700NXXXXXX,1,Dial(IAX2/zorch:zilchnoodle@iaxtel.com/${EXTEN:1}@iaxtel) exten => _91800NXXXXXX,1,Dial(IAX2/zorch:zilchnoodle@iaxtel.com/${EXTEN:1}@iaxtel) [dialgoiax] ignorepat => 9 exten => _93.,1,Set(CALLERID(name)="Joe Worker") exten => _93.,2,Dial(IAX2/878201007658:stickyfinger295@server1.goiax.com/${EXTEN:2}|60|r) exten => _93.,3,Congestion() [homefirst] ignorepat => 9 exten => _91NXXNXXXXXX,1,Macro(ciddial|${EXTEN:1}|${EXTEN:2}|30|TW|Zap/1) exten => _9754XXXX,1,Macro(ciddial|${EXTEN:1}|707${EXTEN:1}|30|TW|Zap/1) exten => _9574XXXX,1,Macro(ciddial|${EXTEN:1}|707${EXTEN:1}|30|TW|Zap/1) exten => _9202XXXX,1,Macro(ciddial|${EXTEN:1}|707${EXTEN:1}|30|TW|Zap/1) exten => _9219XXXX,1,Macro(ciddial|${EXTEN:1}|707${EXTEN:1}|30|TW|Zap/1) exten => _9254XXXX,1,Macro(ciddial|${EXTEN:1}|707${EXTEN:1}|30|TW|Zap/1) exten => _9716XXXX,1,Macro(ciddial|${EXTEN:1}|707${EXTEN:1}|30|TW|Zap/1) exten => _9NXXXXXX,1,Macro(ciddial|1707${EXTEN:1}|707${EXTEN:1}|30|TW|Zap/1) exten => _9011.,1,Macro(ciddial|${EXTEN:1}|${EXTEN:1}|30|TW|Zap/1) exten => _9911,1,Dial(Zap/1/911|30|T) exten => _9411,1,Dial(Zap/1/411|30|T) [workfirst] ignorepat => 9 exten => _91NXXNXXXXXX,1,Macro(ciddial2|${EXTEN:1}|${EXTEN:2}|30|TW|Zap/1) exten => _9754XXXX,1,Macro(ciddial2|${EXTEN:1}|707${EXTEN:1}|30|TW|Zap/1) exten => _9574XXXX,1,Macro(ciddial2|${EXTEN:1}|707${EXTEN:1}|30|TW|Zap/1) exten => _9202XXXX,1,Macro(ciddial2|${EXTEN:1}|707${EXTEN:1}|30|TW|Zap/1) exten => _9219XXXX,1,Macro(ciddial2|${EXTEN:1}|707${EXTEN:1}|30|TW|Zap/1) exten => _9254XXXX,1,Macro(ciddial2|${EXTEN:1}|707${EXTEN:1}|30|TW|Zap/1) exten => _9716XXXX,1,Macro(ciddial2|${EXTEN:1}|707${EXTEN:1}|30|TW|Zap/1) exten => _9NXXXXXX,1,Macro(ciddial2|1707${EXTEN:1}|707${EXTEN:1}|30|TW|Zap/1) exten => _9911,1,Dial(Zap/1/911|30|T) exten => _9411,1,Dial(Zap/1/411|30|T) [force_cell] ignorepat => 8 exten => _81NXXNXXXXXX,1,Macro(ciddial|${EXTEN:1}#|${EXTEN:2}|30|TW|Zap/2) exten => _8754XXXX,1,Macro(ciddial|${EXTEN:1}#|707${EXTEN:1}|30|TW|Zap/2) exten => _8574XXXX,1,Macro(ciddial|${EXTEN:1}#|707${EXTEN:1}|30|TW|Zap/2) exten => _8202XXXX,1,Macro(ciddial|${EXTEN:1}#|707${EXTEN:1}|30|TW|Zap/2) exten => _8219XXXX,1,Macro(ciddial|${EXTEN:1}#|707${EXTEN:1}|30|TW|Zap/2) exten => _8254XXXX,1,Macro(ciddial|${EXTEN:1}#|707${EXTEN:1}|30|TW|Zap/2) exten => _8716XXXX,1,Macro(ciddial|${EXTEN:1}#|707${EXTEN:1}|30|TW|Zap/2) exten => _8NXXXXXX,1,Macro(ciddial|${EXTEN:1}#|707${EXTEN:1}|30|TW|Zap/2) exten => _8911,1,Dial(Zap/1/911|30|T) exten => _8411,1,Dial(Zap/1/411|30|T) [force_home] ignorepat => 8 exten => _81NXXNXXXXXX,1,Macro(ciddial3|${EXTEN:1}#|${EXTEN:2}|30|TW|Zap/1) exten => _8754XXXX,1,Macro(ciddial3|${EXTEN:1}#|707${EXTEN:1}|30|TW|Zap/1) exten => _8574XXXX,1,Macro(ciddial3|${EXTEN:1}#|707${EXTEN:1}|30|TW|Zap/1) exten => _8202XXXX,1,Macro(ciddial3|${EXTEN:1}#|707${EXTEN:1}|30|TW|Zap/1) exten => _8219XXXX,1,Macro(ciddial3|${EXTEN:1}#|707${EXTEN:1}|30|TW|Zap/1) exten => _8254XXXX,1,Macro(ciddial3|${EXTEN:1}#|707${EXTEN:1}|30|TW|Zap/1) exten => _8716XXXX,1,Macro(ciddial3|${EXTEN:1}#|707${EXTEN:1}|30|TW|Zap/1) exten => _8NXXXXXX,1,Macro(ciddial3|1707${EXTEN:1}#|707${EXTEN:1}|30|TW|Zap/1) exten => _8911,1,Dial(Zap/1/911|30|T) exten => _8411,1,Dial(Zap/1/411|30|T) [homeext] ignorepat => 8 ignorepat => 9 include => parkedcalls include => homefirst include => force_cell exten => s,1(loopback),Wait(0) exten => 1,1,Macro(std-priv-exten|Zap/3&Zap/5|2|35|mtw|telemarket|telemarket) exten => 1,2,Goto(s|loopback) exten => 2,1,Macro(std-priv-exten|Zap/6&Zap/5|1|35|mpA(beep3)Tt|telemarket|telemarket) exten => 2,2,Goto(s|loopback) exten => 4,1,VoicemailMain() exten => 5,1,Record(recording:gsm) exten => 5,2,Background(recording) exten => 6,1,Background(recording) exten => 760,1,DateTime() exten => 760,2,Goto(s|loopback) exten => 761,1,Record(announcement:gsm) exten => 761,2,TrySystem(/usr/bin/play /var/lib/asterisk/sounds/announcement.gsm&) exten => 761,3,Goto(s|loopback) exten => 762,1,agi(tts-riddle.agi) exten => 762,2,Background(gsm/what-time-it-is2) exten => 762,3,SayUnixTime() exten => 762,4,Goto(s|loopback) exten => 763,1,Set(CALLERID(num)=) exten => 763,2,Dial(Zap/6r3|35|mptA(beep3)) exten => 763,3,Hangup() exten => 764,1,Set(CALLERID(num)=) exten => 764,2,Dial(Zap/6r3|35|mptnA(beep3)) exten => 764,3,Hangup() exten => 765,1,Set(CALLERID(num)=) exten => 765,2,Dial(Zap/6r3|35|mptNA(beep3)) exten => 765,3,Hangup() exten => 766,1,Dial(Zap/6r3|35|mptNA(beep3)) exten => 766,2,Hangup() exten => 767,1,Dial(Zap/6r3|35|mptnA(beep3)) exten => 767,2,Hangup() exten => 769,1,Playtones(dial) exten => 769,2,Wait(2) exten => 769,3,Playtones(busy) exten => 769,4,Wait(2) exten => 769,5,Playtones(ring) exten => 769,6,Wait(2) exten => 769,7,Playtones(congestion) exten => 769,8,Wait(2) exten => 769,9,Playtones(callwaiting) exten => 769,10,Wait(2) exten => 769,11,Playtones(dialrecall) exten => 769,12,Wait(2) exten => 769,13,Playtones(record) exten => 769,14,Wait(2) exten => 769,15,Playtones(info) exten => 769,16,Wait(5) exten => 769,17,Hangup() exten => 790,1,MeetMe(790|p) exten => 792,1,Goto(pageall|s|begin) exten => 795,1,AGI(wakeup.agi) exten => 795,2,Congestion() exten => 544716,1,TrySystem(/usr/local/bin/who-is-it ${CALLERID(num)} "${CALLERID(name)}"&) exten => 544716,2,Goto(s|loopback) exten => i,1,Background(invalid) exten => i,2,Goto(s|loopback) exten => o,1,Goto(s|loopback) exten => t,1,Congestion() [fromvmhome] exten => 1,1,Dial(Zap/6&Sip/murf|20|Tt) exten => 2,1,Dial(Zap/3&Zap/5|20|Tt) exten => _707202XXXX,1,Macro(ciddial|1${EXTEN:3}|${EXTEN}|30|TW|Zap/1) exten => _707219XXXX,1,Macro(ciddial|1${EXTEN:3}|${EXTEN}|30|TW|Zap/1) exten => _707254XXXX,1,Macro(ciddial|1${EXTEN:3}|${EXTEN}|30|TW|Zap/1) exten => _707716XXXX,1,Macro(ciddial|1${EXTEN:3}|${EXTEN}|30|TW|Zap/1) exten => _707754XXXX,1,Macro(ciddial|${EXTEN:3}|${EXTEN}|30|TW|Zap/1) exten => _707574XXXX,1,Macro(ciddial|${EXTEN:3}|${EXTEN}|30|TW|Zap/1) exten => _NXXNXXXXXX,1,Macro(ciddial|1${EXTEN}|${EXTEN}|30|TW|Zap/1) exten => _1NXXNXXXXXX,1,Macro(ciddial|${EXTEN}|${EXTEN:1}|30|TW|Zap/1) exten => _754XXXX,1,Macro(ciddial|${EXTEN}|707${EXTEN}|30|TW|Zap/1) exten => _574XXXX,1,Macro(ciddial|${EXTEN}|707${EXTEN}|30|TW|Zap/1) exten => _NXXXXXX,1,Macro(ciddial|1707${EXTEN}|707${EXTEN}|30|TW|Zap/1) exten => _911,1,Macro(ciddial|911|911|30|TW|Zap/1) exten => _411,1,Macro(ciddial|411|411|30|TW|Zap/1) [fromvmwork] exten => 1,1,Dial(Zap/6&Sip/murf|20|Tt) exten => 2,1,Dial(Zap/3&Zap/5|20|Tt) exten => _707202XXXX,1,Macro(ciddial|1${EXTEN:3}|${EXTEN}|30|TW|Zap/1) exten => _707219XXXX,1,Macro(ciddial|1${EXTEN:3}|${EXTEN}|30|TW|Zap/1) exten => _707254XXXX,1,Macro(ciddial|1${EXTEN:3}|${EXTEN}|30|TW|Zap/1) exten => _707716XXXX,1,Macro(ciddial|1${EXTEN:3}|${EXTEN}|30|TW|Zap/1) exten => _707754XXXX,1,Macro(ciddial|${EXTEN:3}|${EXTEN}|30|TW|Zap/1) exten => _707574XXXX,1,Macro(ciddial|${EXTEN:3}|${EXTEN}|30|TW|Zap/1) exten => _NXXNXXXXXX,1,Macro(ciddial|1${EXTEN}|${EXTEN}|30|TW|Zap/1) exten => _1NXXNXXXXXX,1,Macro(ciddial|${EXTEN}|${EXTEN:1}|30|TW|Zap/1) exten => _754XXXX,1,Macro(ciddial|${EXTEN}|707${EXTEN}|30|TW|Zap/1) exten => _574XXXX,1,Macro(ciddial|${EXTEN}|707${EXTEN}|30|TW|Zap/1) exten => _NXXXXXX,1,Macro(ciddial|1707${EXTEN}|707${EXTEN}|30|TW|Zap/1) exten => 911,1,Macro(ciddial|911|911|30|TW|Zap/1) exten => 411,1,Macro(ciddial|411|411|30|TW|Zap/1) [fromSeanUniden] include => parkedcalls exten => 21,1,Dial(IAX2/seaniax|20|T) exten => _707202XXXX,1,Macro(ciddial|${EXTEN:3}|${EXTEN}|30|TW|Zap/1) exten => _707219XXXX,1,Macro(ciddial|${EXTEN:3}|${EXTEN}|30|TW|Zap/1) exten => _707254XXXX,1,Macro(ciddial|${EXTEN:3}|${EXTEN}|30|TW|Zap/1) exten => _707716XXXX,1,Macro(ciddial|${EXTEN:3}|${EXTEN}|30|TW|Zap/1) exten => _707754XXXX,1,Macro(ciddial|${EXTEN:3}|${EXTEN}|30|TW|Zap/1) exten => _707574XXXX,1,Macro(ciddial|${EXTEN:3}|${EXTEN}|30|TW|Zap/1) exten => _NXXNXXXXXX,1,Macro(ciddial|1${EXTEN}|${EXTEN}|30|TW|Zap/1) exten => _1NXXNXXXXXX,1,Macro(ciddial|${EXTEN}|${EXTEN:1}|30|TW|Zap/1) exten => _754XXXX,1,Macro(ciddial|${EXTEN}|707${EXTEN}|30|TW|Zap/1) exten => _574XXXX,1,Macro(ciddial|${EXTEN}|707${EXTEN}|30|TW|Zap/1) exten => _NXXXXXX,1,Macro(ciddial|1707${EXTEN}|707${EXTEN}|30|TW|Zap/1) exten => 911,1,Macro(ciddial|911|911|30|TW|Zap/1) exten => 411,1,Macro(ciddial|411|411|30|TW|Zap/1) [workext] ignorepat => 8 ignorepat => 9 include => parkedcalls include => workfirst include => force_home include => dialFWD include => dialiaxtel include => dialgoiax exten => s,1(loopback),Wait(0) exten => 1,1,Dial(Zap/3&Zap/5|20|tT) exten => 2,1,Dial(Zap/5&Zap/6|20|tT) exten => 21,1,Dial(IAX2/seaniax|20|T) exten => 22,1,Set(CALLERID(num)=1234567890) exten => 22,2,Set(CALLERID(name)=TestCaller) exten => 22,3,Dial(Zap/5|20|mP()A(beep)tw) exten => 22,4,NoOp(here is dialstatus: ${DIALSTATUS}...) exten => 22,5,Goto(s|loopback) exten => 4,1,VoicemailMain() exten => 4,2,Goto(s|loopback) exten => 5,1,Record(recording:gsm) exten => 5,2,Background(recording) exten => 6,1,ZapBarge() exten => 760,1,DateTime() exten => 760,2,Goto(s|loopback) exten => 761,1,ZapBarge() exten => 761,2,Goto(s|loopback) exten => 765,1,Playback(demo-echotest) exten => 765,2,Echo() exten => 765,3,Playback(demo-echodone) exten => 765,4,Goto(s|loopback) exten => 766,1,Festival(The other thing to watch is neuro-electronics: the ability to interface technology with our neural system: My wife: Sigrid: has had a cochlear implant since 1996. This once profoundly deaf person now uses the phone: recognizes accents: and listens to movies and recorded books.) exten => 766,2,Goto(s|loopback) exten => 767,1,agi(tts-riddle.agi) exten => 767,2,Background(gsm/what-time-it-is2) exten => 767,3,SayUnixTime() exten => 767,4,Goto(s|loopback) exten => 768,1,agi(tts-computer.agi) exten => 771,1,eagi(eagi-test) exten => 771,2,agi(my-agi-test) exten => 772,1,agi(wakeup.agi) exten => 775,1,GotoIf($[${EXTEN}=${EXTEN} ]?2:4) exten => 775,2,BackGround(digits/1) exten => 775,3,Goto(5) exten => 775,4,BackGround(digits/0) exten => 775,5,NoOp(Finish if-workext-108) exten => 775,6,GotoIf($[${EXTEN}=${LANGUAGE} ]?7:9) exten => 775,7,BackGround(digits/1) exten => 775,8,Goto(10) exten => 775,9,BackGround(digits/0) exten => 775,10,NoOp(Finish if-workext-109) exten => 775,11,BackGround(digits/2) exten => 776,1,Set(TEST=00359889811777) exten => 776,2,GotoIf($[${TEST}= 00359889811777 ]?3:5) exten => 776,3,BackGround(digits/1) exten => 776,4,Goto(6) exten => 776,5,BackGround(digits/0) exten => 776,6,NoOp(Finish if-workext-110) exten => 776,7,GotoIf($[${TEST}= 00359889811888 ]?8:10) exten => 776,8,BackGround(digits/1) exten => 776,9,Goto(11) exten => 776,10,BackGround(digits/0) exten => 776,11,NoOp(Finish if-workext-111) exten => 776,12,Hangup() exten => 790,1,MeetMe(790|p) exten => 792,1,Goto(pageall|s|begin) exten => 793,1,NoOp(Hello| this is included from include1.ael2) exten => 793,2,NoOp(This was included from include2.ael2) exten => 793,3,NoOp(This is include3.ael2!) exten => 793,4,NoOp(Include5.ael2 doesn't include anything| either!) exten => 793,5,NoOp(This is include4.ael2! Isn't it cool!?!?!?!) exten => 793,6,NoOp(4 doesn't include anything) exten => 795,1,AGI(wakeup.agi) exten => 795,2,Congestion() exten => 797,1,Set(CONFCIDNA=${CALLERID(name)}) exten => 797,2,Set(CONFCIDNU=${CALLERID(num)}) exten => 797,3,AGI(callall) exten => 797,4,AGI(submit-announce.agi) exten => 797,5,Hangup() [wakeup] exten => 3,1,Dial(Zap/3|30) exten => 4,1,Dial(Zap/4|30) exten => 5,1,Dial(Zap/5|30) exten => 6,1,Dial(Zap/6|30) exten => 99,1,Dial(IAX2/murfiaxphone|30) exten => 97,1,Dial(IAX2/ryaniax|30) exten => 94,1,Dial(IAX2/seaniax|30) [announce-all] exten => s,1(begin),MeetMe(5555|dtqp) exten => s,2,MeetMeAdmin(5555|K) exten => s,3,Hangup() exten => h,1,MeetMeAdmin(5555|K) exten => h,2,Hangup() [telemarket] exten => s,1(begin),Playback(telemarketer-intro) exten => s,2,Playback(telemarketer-choices) exten => 1,1,Goto(telemarket-charity|s|begin) exten => 2,1,Goto(telemarket-political|s|begin) exten => 3,1,Goto(telemarket-pollster|s|begin) exten => 4,1,Goto(telemarket-research|s|begin) exten => 5,1,Goto(telemarket-magazine|s|begin) exten => 6,1,Goto(telemarket-commercial|s|begin) exten => 7,1,Goto(telemarket-other|s|begin) exten => t,1,Goto(telemarket|s|begin) exten => i,1,Goto(telemarket|s|begin) exten => o,1,Goto(telemarket|s|begin) [telemarket-charity] exten => s,1(begin),Playback(telemark-charity-intro) exten => s,2,Playback(telemark-charity-choices) exten => 1,1,Goto(telemarket-char-disease|s|begin) exten => 2,1,Goto(telemarket-char-handicap|s|begin) exten => 3,1,Goto(telemarket-char-police|s|begin) exten => 4,1,Goto(telemarket-char-school|s|begin) exten => 5,1,Goto(telemarket-char-college|s|begin) exten => 6,1,Goto(telemarket-char-animal|s|begin) exten => 7,1,Goto(telemarket-char-candidate|s|begin) exten => 8,1,Goto(telemarket-char-abuse|s|begin) exten => 9,1,Goto(telemarket-char-other|s|begin) exten => t,1,Goto(telemarket|s|begin) exten => i,1,Goto(telemarket|s|begin) exten => o,1,Goto(telemarket|s|begin) [telemarket-char-disease] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-char-handicap] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-char-police] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-char-school] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-char-college] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-char-animal] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-char-candidate] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-char-abuse] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-char-other] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-sorry] exten => s,1(begin),Playback(telemarket-sorry) exten => s,2,Hangup() [telemarket-exception] exten => s,1(begin),Playback(telemarket-success) exten => s,2,Hangup() [telemarket-political] exten => s,1(begin),Playback(telemark-polit-intro) exten => s,2,Playback(telemark-polit-choices) exten => 1,1,Goto(telemarket-poli-Am1st|s|begin) exten => 2,1,Goto(telemarket-poli-American|s|begin) exten => 3,1,Goto(telemarket-poli-AmHer|s|begin) exten => 4,1,Goto(telemarket-poli-AmInd|s|begin) exten => 5,1,Goto(telemarket-poli-AmNaz|s|begin) exten => 6,1,Goto(telemarket-poli-Pot|s|begin) exten => 7,1,Goto(telemarket-poli-AmRef|s|begin) exten => 8,1,Goto(telemarket-poli-CFP|s|begin) exten => 9,1,Goto(telemarket-political2|s|begin) exten => t,1,Goto(telemarket|s|begin) exten => i,1,Goto(telemarket|s|begin) exten => o,1,Goto(telemarket|s|begin) [telemarket-political2] exten => s,1(begin),Playback(telemark-politx-intro) exten => s,2,Playback(telemark-polit2-choices) exten => 1,1,Goto(telemarket-poli-Communist|s|begin) exten => 2,1,Goto(telemarket-poli-Constit|s|begin) exten => 3,1,Goto(telemarket-poli-FamVal|s|begin) exten => 4,1,Goto(telemarket-poli-FreedSoc|s|begin) exten => 5,1,Goto(telemarket-poli-Grassroot|s|begin) exten => 6,1,Goto(telemarket-poli-Green|s|begin) exten => 7,1,Goto(telemarket-poli-Greens|s|begin) exten => 8,1,Goto(telemarket-poli-Independence|s|begin) exten => 9,1,Goto(telemarket-political3|s|begin) exten => t,1,Goto(telemarket|s|begin) exten => i,1,Goto(telemarket|s|begin) exten => o,1,Goto(telemarket|s|begin) [telemarket-political3] exten => s,1(begin),Playback(telemark-politx-intro) exten => s,2,Playback(telemark-polit3-choices) exten => 1,1,Goto(telemarket-poli-IndAm|s|begin) exten => 2,1,Goto(telemarket-poli-Labor|s|begin) exten => 3,1,Goto(telemarket-poli-Liber|s|begin) exten => 4,1,Goto(telemarket-poli-Light|s|begin) exten => 5,1,Goto(telemarket-poli-NatLaw|s|begin) exten => 6,1,Goto(telemarket-poli-New|s|begin) exten => 7,1,Goto(telemarket-poli-NewUn|s|begin) exten => 8,1,Goto(telemarket-poli-PeaceFree|s|begin) exten => 9,1,Goto(telemarket-political4|s|begin) exten => t,1,Goto(telemarket|s|begin) exten => i,1,Goto(telemarket|s|begin) exten => o,1,Goto(telemarket|s|begin) [telemarket-political4] exten => s,1(begin),Playback(telemark-politx-intro) exten => s,2,Playback(telemark-polit4-choices) exten => 1,1,Goto(telemarket-poli-Prohib|s|begin) exten => 2,1,Goto(telemarket-poli-Ref|s|begin) exten => 3,1,Goto(telemarket-poli-Revol|s|begin) exten => 4,1,Goto(telemarket-poli-SocPart|s|begin) exten => 5,1,Goto(telemarket-poli-SocAct|s|begin) exten => 6,1,Goto(telemarket-poli-SocEq|s|begin) exten => 7,1,Goto(telemarket-poli-SocLab|s|begin) exten => 8,1,Goto(telemarket-poli-SocWork|s|begin) exten => 9,1,Goto(telemarket-political5|s|begin) exten => t,1,Goto(telemarket|s|begin) exten => i,1,Goto(telemarket|s|begin) exten => o,1,Goto(telemarket|s|begin) [telemarket-political5] exten => s,1(begin),Playback(telemark-politx-intro) exten => s,2,Playback(telemark-polit5-choices) exten => 1,1,Goto(telemarket-poli-South|s|begin) exten => 2,1,Goto(telemarket-poli-SoInd|s|begin) exten => 3,1,Goto(telemarket-poli-USPac|s|begin) exten => 4,1,Goto(telemarket-poli-WTP|s|begin) exten => 5,1,Goto(telemarket-poli-WWP|s|begin) exten => 6,1,Goto(telemarket-poli-Democrat|s|begin) exten => 7,1,Goto(telemarket-poli-Repub|s|begin) exten => 8,1,Goto(telemarket-poli-other|s|begin) exten => t,1,Goto(telemarket|s|begin) exten => i,1,Goto(telemarket|s|begin) exten => o,1,Goto(telemarket|s|begin) [telemarket-poli-other] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-poli-Repub] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-poli-Democrat] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-poli-WWP] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-poli-WTP] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-poli-USPac] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-poli-SoInd] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-poli-South] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-poli-SocWork] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-poli-SocLab] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-poli-SocEq] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-poli-SocAct] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-poli-SocPart] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-poli-Revol] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-poli-Ref] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-poli-Prohib] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-poli-PeaceFree] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-poli-NewUn] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-poli-New] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-poli-NatLaw] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-poli-Light] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-poli-Liber] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-poli-Labor] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-poli-IndAm] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-poli-Independence] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-poli-Greens] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-poli-Green] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-poli-Grassroot] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-poli-FreedSoc] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-poli-FamVal] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-poli-Constit] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-poli-Communist] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-poli-CFP] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-poli-AmRef] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-poli-Pot] exten => s,1(begin),Goto(telemarket-political|s|begin) [telemarket-poli-AmNaz] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-poli-AmInd] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-poli-AmHer] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-poli-American] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-poli-Am1st] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-pollster] exten => s,1(begin),Playback(telemark-poll-intro) exten => s,2,Goto(telemarket-sorry|s|begin) exten => t,1,Goto(telemarket|s|begin) exten => i,1,Goto(telemarket|s|begin) exten => o,1,Goto(telemarket|s|begin) [telemarket-research] exten => s,1(begin),Playback(telemark-research-intro) exten => s,2,Goto(telemarket-sorry|s|begin) exten => t,1,Goto(telemarket|s|begin) exten => i,1,Goto(telemarket|s|begin) exten => o,1,Goto(telemarket|s|begin) [telemarket-magazine] exten => s,1(begin),Playback(telemark-mag-choices) exten => 1,1,Goto(telemark-mag-new|s|begin) exten => 2,1,Goto(telemark-mag-renew|s|begin) exten => 3,1,Goto(telemark-mag-survey|s|begin) exten => 4,1,Goto(telemark-mag-verify|s|begin) exten => 5,1,Goto(telemark-mag-other|s|begin) exten => t,1,Goto(telemarket|s|begin) exten => i,1,Goto(telemarket|s|begin) exten => o,1,Goto(telemarket|s|begin) [telemark-mag-new] exten => s,1(begin),Playback(telemark-mag-new) exten => s,2,Hangup() exten => t,1,Goto(telemarket|s|begin) exten => i,1,Goto(telemarket|s|begin) exten => o,1,Goto(telemarket|s|begin) [telemark-mag-renew] exten => s,1(begin),Playback(telemark-mag-renew) exten => s,2,Hangup() exten => t,1,Goto(telemarket|s|begin) exten => i,1,Goto(telemarket|s|begin) exten => o,1,Goto(telemarket|s|begin) [telemark-mag-survey] exten => s,1(begin),Playback(telemark-mag-survey) exten => s,2,Hangup() exten => t,1,Goto(telemarket|s|begin) exten => i,1,Goto(telemarket|s|begin) exten => o,1,Goto(telemarket|s|begin) [telemark-mag-verify] exten => s,1(begin),Playback(telemark-mag-verify) exten => s,2,Hangup() exten => t,1,Goto(telemarket|s|begin) exten => i,1,Goto(telemarket|s|begin) exten => o,1,Goto(telemarket|s|begin) [telemark-mag-other] exten => s,1(begin),Goto(telemarket-sorry|s|begin) [telemarket-commercial] exten => s,1(begin),Playback(telemark-comm-intro) exten => s,2,Voicemail(u82) exten => s,3,Goto(telemarket-sorry|s|begin) exten => t,1,Goto(telemarket|s|begin) exten => i,1,Goto(telemarket|s|begin) exten => o,1,Goto(telemarket|s|begin) [telemarket-other] exten => s,1(begin),Playback(telemark-other-intro) exten => s,2,Hangup() exten => t,1,Goto(telemarket|s|begin) exten => i,1,Goto(telemarket|s|begin) exten => o,1,Goto(telemarket|s|begin) asterisk-1.4.21.2/pbx/ael/ael-test/ref.ael-test180000644000175000017500000000211511021261300020444 0ustar maniacmaniac (If you find progress and other non-error messages irritating, you can use -q to suppress them) (You can use the -w option to dump extensions.conf format to extensions.conf.aeldump) LOG: lev:2 file:pbx_ael.c line:4131 func: pbx_load_module Starting AEL load process. LOG: lev:2 file:pbx_ael.c line:4138 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:4146 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:4149 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:4151 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:4154 func: pbx_load_module AEL load process: merged config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:4157 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. LOG: lev:4 file:ael2_parse line:543 func: main 1 contexts, 7 extensions, 29 priorities asterisk-1.4.21.2/pbx/ael/ael-test/ael-test20/0000755000175000017500000000000011041455736017764 5ustar maniacmaniacasterisk-1.4.21.2/pbx/ael/ael-test/ael-test20/extensions.ael0000644000175000017500000000024410616372261022644 0ustar maniacmaniaccontext interesting { eswitches { Realtime/default@extensions; IAX2/context@${CURSERVER}; } 13 => NoOp(LuckyNumber!); } asterisk-1.4.21.2/pbx/ael/ael-test/ael-ntest10/0000755000175000017500000000000011041455740020134 5ustar maniacmaniacasterisk-1.4.21.2/pbx/ael/ael-test/ael-ntest10/extensions.ael0000644000175000017500000000423710465634503023031 0ustar maniacmaniacmacro endsess() { NoOp(hithere); } macro nullchk(type) { NoOp(${type} is this); } macro endcall(type) { switch(${type}) { case out: &nullchk(callid); if(${testnotnull}) { &endsess(); goto ptr1 ; // <-- goto call to valid label } else { ptr1: // <-- valid label Softhangup(${CHANNEL}); break ; } Noop(esac) ; } } macro endcall2(type) { switch(${type}) { case out: &nullchk(callid); if(${testnotnull}) { &endsess(); goto ptr1 ; // <-- goto call to valid label } case out2: { ptr1: // <-- valid label Softhangup(${CHANNEL}); break ; } Noop(esac) ; } } macro endcall3(type) { switch(${type}) { case out: &nullchk(callid); if(${testnotnull}) { &endsess(); goto ptr1 ; // <-- goto call to valid label } Noop(esac) ; } if(${testnotnull}) { goto ptr1; } switch(${type}) { case out: if(${testnotnull}) { ptr1: // <-- valid label Softhangup(${CHANNEL}); break ; } Noop(esac) ; } } macro endcall4(type) { switch(${type}) { case out: &nullchk(callid); if(${testnotnull}) { &endsess(); goto ptr1 ; // <-- goto call to valid label } Noop(esac) ; } if(${testnotnull}) { goto ptr1; } switch(${type}) { case out: switch(${type}) { case in: if(${testnotnull}) { ptr1: // <-- valid label Softhangup(${CHANNEL}); break ; } Noop(esac) ; } } } macro endcall5(type) { switch(${type}) { case out: &nullchk(callid); if(${testnotnull}) { &endsess(); goto ptr1 ; // <-- goto call to valid label } case in: &nullchk(callid); ptr2: if(${testnotnull}) { &endsess(); goto ptr1 ; // <-- goto call to valid label } Noop(esac) ; } if(${testnotnull}) { goto ptr1; } switch(${type}) { case out: switch(${type}) { case in: if(${testnotnull}) { ptr1: // <-- valid label Softhangup(${CHANNEL}); break ; } Noop(esac) ; } } } asterisk-1.4.21.2/pbx/ael/ael-test/ael-test5/0000755000175000017500000000000011041455740017702 5ustar maniacmaniacasterisk-1.4.21.2/pbx/ael/ael-test/ael-test5/extensions.ael0000644000175000017500000004351510423206707022574 0ustar maniacmaniac/////////////////////////////////////////////////////////////////////////////// // Helpdesk Queue context hd-queue { s => { NoOp(Add a background sound to tell the user their options); Queue(helpdesk|t); NoOp(Put in options to apologize and send user to voicemail); }; 0 => goto default|0|1; 1 => { Dial(u41950@svm1.shsu.edu); Congestion(10); Hangup; }; }; context l903-calling { _9903NXXXXXX => { Realtime(l903_ext,exchange,${EXTEN:4:3},l903_); if ("${l903_exchange}foo" = "foo") { Playback(num-outside-area); SayDigits(1); Playback(and-area-code); Playback(before-the-number); Hangup; }; &dialout(${EXTEN}); Congestion(10); Hangup; }; }; /////////////////////////////////////////////////////////////////////////////// // Extensions pulled from houston.conf // Converted the extension list to the database context houston-calling { _9713NXXXXXX => { Realtime(hou_713_ext,exchange,${EXTEN:4:3},hou_713_); if ("${hou_713_exchange}foo" = "foo") { Playback(num-outside-area); SayDigits(1); Playback(and-area-code); Playback(before-the-number); Hangup; }; &dialout(${EXTEN}); Congestion(10); Hangup; }; _9281NXXXXXX => { Realtime(hou_281_ext,exchange,${EXTEN:4:3},hou_281_); if ("${hou_281_exchange}foo" = "foo") { Playback(num-outside-area); SayDigits(1); Playback(and-area-code); Playback(before-the-number); Hangup; }; &dialout(${EXTEN}); Congestion(10); Hangup; }; _9832NXXXXXX => { Realtime(hou_832_ext,exchange,${EXTEN:4:3},hou_832_); if ("${hou_832_exchange}foo" = "foo") { Playback(num-outside-area); SayDigits(1); Playback(and-area-code); Playback(before-the-number); Hangup; }; &dialout(${EXTEN}); Congestion(10); Hangup; }; }; /////////////////////////////////////////////////////////////////////////////// // Extensions pulled from huntsville.conf // Converted the extension list to the database context huntsville-calling { _9NXXXXXX => { Realtime(hv_ext,exchange,${EXTEN:1:3},hv_); if ("${hv_exchange}foo" = "foo") { Playback(num-outside-area); SayDigits(1); Playback(and-area-code); Playback(before-the-number); Hangup; }; &dialout(${EXTEN}); Congestion(10); Hangup; }; _NXXXXXX => { NoOp(Stripping last four to see what extension we're dialing); Set(LAST4=${EXTEN:3}); StripLSD(4); }; i => Playback(pbx-invalid); h => Hangup; }; /////////////////////////////////////////////////////////////////////////////// // Extensions pulled from macros.conf macro dialout( number ) { Realtime(call_info,exten,${CALLERIDNUM:5},mon_); if ("${mon_monitor}" = "YES") { Dial(SIP/${number}@sgw1.shsu.edu,,wW); Dial(SIP/${number}@sgw2.shsu.edu,,wW); } else { Dial(SIP/${number}@sgw1.shsu.edu); Dial(SIP/${number}@sgw2.shsu.edu); }; }; // Standard extension macro: // ${ext} - Extension macro stdexten( ext ) { Realtime(sipusers,name,${ext},sip_user_); Realtime(call_info,exten|${ext},info_); if ("${sip_user_name}foo" = "foo") { Wait(1); &dialout(${ext}); Congestion(10); Hangup; }; NoOp(${CALLERIDNUM}); RealtimeUpdate(call_info,exten,${ext},calltrace,${CALLERIDNUM}); System(/usr/local/bin/db_update.sh call_info calltrace ${CALLERIDNUM} exten ${ext} &); &checkdnd(${ext}); &checkcf(${ext}); Realtime(call_info,exten,${CALLERIDNUM:5},mon_); if ("${mon_monitor}" = "YES") { Dial(SIP/${info_forwardto},25,wW); } else { Dial(SIP/${info_forwardto},25); }; switch ("${DIALSTATUS}") { case "BUSY": &checkcfb(${ext}); break; case "CHANUNAVAIL": Dial(IAX2/asterisk:password@scm2.shsu.edu/${info_forwardto},25,wW); MailboxExists(${ext}); // if ("${VMBOXEXISTSSTATUS}" = "FAILED") { // Congestion(10); // Hangup; // }; &uvm(${ext}); Hangup; break; case "CONGESTION": MailboxExists(${ext}); if ("${VMBOXEXISTSSTATUS}" = "FAILED") { Congestion(10); Hangup; }; &bvm(${ext}); Hangup; break; default: MailboxExists(${ext}); if ("${VMBOXEXISTSSTATUS}" = "FAILED") { Congestion(10); Hangup; }; &uvm(${ext}); Hangup; }; Hangup; }; macro uvm( ext ) { Dial(SIP/u${ext}@svm1.shsu.edu); Playback(im-sorry); Playback(voice-mail-system); Playback(down); Congestion(10); Hangup; }; macro bvm( ext ) { Dial(SIP/b${ext}@svm1.shsu.edu); Playback(im-sorry); Playback(voice-mail-system); Playback(down); Congestion(10); Hangup; }; macro checkdnd( ext ) { if ("${info_donotdisturb}foo" = "foo") { NoOp(Do Not Disturb is not active); } else &uvm(${ext}); }; macro checkcf( ext ) { if ("${info_forwardto}foo" = "foo") if ("${ext}" = "43974") { Set(info_forwardto=${ext}&SCCP/${ext}); } else { Set(info_forwardto=${ext}&SIP/${ext}w); }; }; macro checkcfb( ext ) { if ("${info_forwardbusy}foo" = "foo") { Wait(1); MailboxExists(${ext}); if ("${VMBOXEXISTSSTATUS}" = "FAILED") { &dialout(${ext}); Hangup; }; &bvm(${ext}); Hangup; }; &stdexten(${info_forwardbusy}); }; /////////////////////////////////////////////////////////////////////////////// // Extensions pulled from test.conf context test-include { includes { test-digium; test-sounds; test-phinfo; }; }; context test-digium { *500 => { Dial(IAX2/guest@misery.digium.com/s@default); Playback(demo-nogo); Hangup; }; }; context test-sounds { *501 => { Answer; Musiconhold; Wait(1); Hangup; }; }; context test-phinfo { *505 => { Answer; NoOp(${CALLERIDNUM:5}); SayDigits(${CALLERIDNUM:5}); Hangup; }; }; /////////////////////////////////////////////////////////////////////////////// // Extensions pulled from external.conf context long-distance { includes { local; }; _91XXXXXXXXXX => &dialout(${EXTEN}); _9011. => &dialout(${EXTEN}); }; context local { includes { default; }; 911 => &dialout(911); 9911 => &dialout(9911); _9NXXXXXX => goto huntsville-calling|${EXTEN}|1; _936NXXXXXX => { goto 9${EXTEN:3}|1; Congestion(10); Hangup; }; _832NXXXXXX => { goto 9${EXTEN}|1; Congestion(10); Hangup; }; _713NXXXXXX => { goto 9${EXTEN}|1 ; Congestion(10); Hangup; }; _281NXXXXXX => { goto 9${EXTEN}|1; Congestion(10); Hangup; }; _NXXNXXXXXX => { goto 9${EXTEN}|1; goto 91${EXTEN}|1; Congestion(10); Hangup; }; _91800NXXXXXX => &dialout(${EXTEN}); _91866NXXXXXX => &dialout(${EXTEN}); _91877NXXXXXX => &dialout(${EXTEN}); _91888NXXXXXX => &dialout(${EXTEN}); _91900NXXXXXX => &dialout(${EXTEN}); _91976NXXXXXX => &dialout(${EXTEN}); _9713NXXXXXX => goto houston-calling|${EXTEN}|1; _9281NXXXXXX => goto houston-calling|${EXTEN}|1; _9832NXXXXXX => goto houston-calling|${EXTEN}|1; _9903NXXXXXX => goto l903-calling|${EXTEN}|1; _31NXXNXXXXXX => &dialout(${EXTEN}); h => Hangup; }; /////////////////////////////////////////////////////////////////////////////// // Extensions pulled from internal.conf context from-scm2 { _4XXXX => { NoOp(DIALING SIP EXTENSION ${EXTEN} - FROM ${CALLERIDNUM}); Dial(SIP/${EXTEN},20,wW); Hangup; }; _6XXXX => { NoOp(DIALING SIP EXTENSION ${EXTEN} - FROM ${CALLERIDNUM}); Dial(SIP/${EXTEN},20,wW); Hangup; }; }; /////////////////////////////////////////////////////////// // All internal extensions work through the default context // Phones that can only make internal calls should be in // this context. /////////////////////////////////////////////////////////// context default { // Include the contexts in the files that allow us to make these phone calls includes { vm-include; apps-include; test-include; }; // ALWAYS have an 'h' extension h => { NoOp(Hangup cause was: ${HANGUPCAUSE}); Hangup; }; // We like to hear that we dialed an invalid extension i => Playback(pbx-invalid); // Dial the operator 0 => &dialout(0); // Send voicemail calls to the vm-* contexts to be handled voicemail => goto vm-direct|s|1; 5555 => goto vm-direct|s|1; 62100 => goto vm-extension|s|1; // These are our campus extensions, send them to the macro _6XXXX => &stdexten(${EXTEN}); _4XXXX => &stdexten(${EXTEN}); // These are campus extensions as well, might need to take this out though. _9294XXXX => goto _4XXXX|1; _9496XXXX => goto _6XXXX|1; // These allows us to dial from the directory in our phone without worrying about dialing 9 _936294XXXX => { goto ${EXTEN:5}|1; goto 9${EXTEN:3}|1; Congestion(10); Hangup; }; _936496XXXX => { goto ${EXTEN:5}|1; goto 9${EXTEN:3}|1; Congestion(10); Hangup; }; }; /////////////////////////////////////////////////////////////////////////////// // Extensions pulled from apps.conf context apps-include { includes { app-agents; app-dnd; app-callforward; app-calltrace; app-conferences; app-ssd; app-psd; app-idblock; app-helpdesk; app-dictate; app-set-monitor; }; }; context app-agents { *54 => { Answer; Wait(1); Read(agent_no|agent-user); AgentCallbackLogin(${agent_no}|s${CALLERIDNUM:5}); Playback(agent-loginok); Hangup; }; *55 => { Answer; Wait(1); AgentCallbackLogin(${agent_no}); Hangup; }; }; context app-calltrace { // caller dials this to find out the last call missed and possibly call back *69 => goto app-calltrace-perform|s|1; }; context app-calltrace-perform { s => { Answer; Wait(1); Background(info-about-last-call); Background(telephone-number); RealTime(call_info|exten|${CALLERIDNUM:5}|ct_); if ("${ct_calltrace}foo" = "foo") { Playback(loligo/from-unknown-caller); Hangup; } else { SayDigits("${ct_calltrace}"); Set(TIMEOUT(digit)=3); Set(TIMEOUT(response)=7); Background(loligo/to-call-this-number); Background(press-1); Background(loligo/silence/5); }; }; 1 => goto local|${ct_calltrace}|1; i => { Playback(vm-goodbye); Hangup; }; t => { Playback(vm-goodbye); Hangup; }; }; context app-set-monitor { *50 => { Realtime(call_info,exten,${CALLERIDNUM:5},mon_set_); if ("${mon_set_monitor}" = "YES") { RealtimeUpdate(call_info,exten,${CALLERIDNUM:5},monitor|); System(/usr/local/bin/db_update.sh call_info monitor '' exten ${CALLERIDNUM:5} &); } else { RealtimeUpdate(call_info,exten,${CALLERIDNUM:5},monitor,YES); System(/usr/local/bin/db_update.sh call_info monitor YES exten ${CALLERIDNUM:5} &); }; NoOp(${mon_set_monitor}); Hangup; }; }; context app-dnd { *78 => { Answer; Wait(1); RealtimeUpdate(call_info,exten,${CALLERIDNUM:5},donotdisturb,YES); System(/usr/local/bin/db_update.sh call_info donotdisturb YES exten ${CALLERIDNUM:5} &); Playback(do-not-disturb); Playback(loligo/activated); Hangup; }; *79 => { Answer; Wait(1); RealtimeUpdate(call_info,exten,${CALLERIDNUM:5},donotdisturb|); System(/usr/local/bin/db_update.sh call_info donotdisturb '' exten ${CALLERIDNUM:5} &); Playback(do-not-disturb); Playback(loligo/de-activated); Hangup; }; }; context app-callforward { // forwards calling extension to input number *72{EXTEN} _*72. => { RealtimeUpdate(call_info,exten,${CALLERIDNUM:5},forwardto,${EXTEN:3}); System(/usr/local/bin/db_update.sh call_info forwardto ${EXTEN:3} exten ${CALLERIDNUM:5} &); Answer; Wait(1); Playback(loligo/call-fwd-unconditional); Playback(loligo/for); Playback(loligo/extension); SayDigits(${CALLERIDNUM:5}); Playback(loligo/is-set-to); SayDigits(${EXTEN:3}); Hangup; }; // prompts for extension to forward to *72 => { Answer; Wait(1); Playback(please-enter-your); Playback(extension); Background(then-press-pound); VMAuthenticate(|s); Background(loligo/ent-target-attendant); Read(toext,loligo/then-press-pound); Wait(1); RealtimeUpdate(call_info,exten,${AUTH_MAILBOX},forwardto,${toext}); System(/usr/local/bin/db_update.sh call_info forwardto ${toext} exten ${AUTH_MAILBOX} &); Playback(loligo/call-fwd-unconditional); Playback(loligo/for); Playback(loligo/extension); SayDigits(${AUTH_MAILBOX}); Playback(loligo/is-set-to); SayDigits(${toext}); Hangup; }; // cancels dialed extension call forward _*73. => { Realtime(voicemail,mailbox,${EXTEN:3},auth_); Answer; Wait(1); Authenticate(${auth_password}); RealtimeUpdate(call_info,exten,${EXTEN:3},forwardto,); System(/usr/local/bin/db_update.sh call_info forwardto '' exten ${EXTEN:3} &); Wait(1); SayDigits(${EXTEN:3}); Playback(loligo/call-fwd-cancelled); Hangup; }; // cancels call forward for calling extension *73 => { RealtimeUpdate(call_info,exten,${CALLERIDNUM:5},forwardto,); System(/usr/local/bin/db_update.sh call_info forwardto '' exten ${CALLERIDNUM:5} &); Answer; Wait(1); Playback(loligo/call-fwd-cancelled); Hangup; }; // dialed call forward on busy _*90. => { RealtimeUpdate(call_info,exten,${CALLERIDNUM:5},forwardbusy,${EXTEN:3}); System(/usr/local/bin/db_update.sh call_info forwardbusy ${EXTEN:3} exten ${CALLERIDNUM:5} &); Answer; Wait(1); Playback(loligo/call-fwd-on-busy); Playback(loligo/for); Playback(loligo/extension); SayDigits(${CALLERIDNUM:5}); Playback(loligo/is-set-to); SayDigits(${EXTEN:3}); Hangup; }; // cancels call forward on busy for calling extension *91 => { RealtimeUpdate(call_info,exten,${CALLERIDNUM:5},forwardbusy|); System(/usr/local/bin/db_update.sh call_info forwardbusy '' exten ${CALLERIDNUM:5} &); Answer; Wait(1); Playback(loligo/call-fwd-on-busy); Playback(loligo/de-activated); Hangup; }; h => Hangup; }; context app-idblock { _*67. => { Set(CALLERID(name)=Anonymous); &stdexten(${EXTEN:3}); }; }; context app-dictate { *1 => { Dictate(); Hangup; }; }; context app-ssd { // *59 - Set system speed dial to digits // *59 0 - Delete system speed dial // *59 - Review system speed dial // *1xx - Dial speed dial _*59XXX. => { Answer; RealtimeUpdate(ssd,sd,${EXTEN:3:2},extension,${EXTEN:5}); System(/usr/local/bin/db_update.sh systemsd extension ${EXTEN:5} sd ${EXTEN:3:2} &); Wait(1); Playback(loligo/speed-dial); SayDigits(${EXTEN:3:2}); Playback(loligo/has-been-set-to); SayDigits(${EXTEN:5}); Hangup; }; _*59XX0 => { Answer; RealtimeUpdate(ssd,sd,${EXTEN:3:2},extension,); System(/usr/local/bin/db_update.sh systemsd extension '' sd ${EXTEN:3:2} &); Wait(1); Playback(loligo/speed-dial); SayDigits(${EXTEN:3:2}); Playback(loligo/has-been-cleared); Hangup; }; _*59XX => { Answer; Realtime(ssd,sd,${EXTEN:3},ssd_); if ("${ssd_extension}foo" = "foo") { Playback(loligo/speed-dial); SayDigits(${EXTEN:3:2}); Playback(loligo/is-not-set); Hangup; }; Wait(1); Playback(loligo/speed-dial); SayDigits(${EXTEN:3:2}); Playback(loligo/is-set-to); SayDigits(${ssd_extension}); Hangup; }; // NTC = number to call _*1XX => { Realtime(ssd,sd,${EXTEN:2},ssd_); if ("${ssd_extension}foo" = "foo") { Answer; Wait(1); Playback(loligo/speed-dial); SayDigits(${EXTEN:2}); Playback(loligo/is-not-set); Hangup; }; &stdexten(${ssd_extension}); Congestion(10); Hangup; }; }; macro check-psd-exists ( ext ) { Realtime(psd,extension,${ext},psd_); if ("${psd_extension}foo" = "foo") { System(/usr/local/bin/create_psd.sh ${ext}); } else NoOp(PSD set for ${ext}); }; context app-psd { // *89 - Set personal speed dial to digits // *89 0 - Delete personal speed dial // *89 - Review personal speed dial // *2xx - Dial personal speed dial _*89XXX. => { &check-psd-exists(${CALLERIDNUM:5}); Answer; RealtimeUpdate(psd,extension,${CALLERIDNUM:5},s${EXTEN:3:2},${EXTEN:5}); System(/usr/local/bin/db_update.sh personalsd s${EXTEN:3:2} ${EXTEN:5} extension ${CALLERIDNUM:5} &); Wait(1); Playback(loligo/speed-dial); SayDigits(${EXTEN:3:2}); Playback(loligo/has-been-set-to); SayDigits(${EXTEN:5}); Hangup; }; _*89XX0 => { &check-psd-exists(${CALLERIDNUM:5}); Answer; RealtimeUpdate(psd|extension|${CALLERIDNUM:5}|s${EXTEN:3:2}|); System(/usr/local/bin/db_update.sh personalsd s${EXTEN:3:2} '' extension ${CALLERIDNUM:5} &); Wait(1); Playback(loligo/speed-dial); SayDigits(${EXTEN:3:2}); Playback(loligo/has-been-cleared); Hangup; }; _*89XX => { &check-psd-exists(${CALLERIDNUM:5}); Answer; Realtime(psd|extension|${CALLERIDNUM:5}|psd_); Wait(1); if ("${psd_s${EXTEN:3:2}}foo" = "foo") { Playback(loligo/speed-dial); SayDigits(${EXTEN:3:2}); Playback(loligo/is-not-set); Hangup; }; Playback(loligo/speed-dial); SayDigits(${EXTEN:3:2}); Playback(loligo/is-set-to); SayDigits(${psd_s${EXTEN:3:2}}); Hangup; }; // NTC = number to call _*2XX => { &check-psd-exists(${CALLERIDNUM:5}); Realtime(psd|extension|${CALLERIDNUM:5}|psd_); if ("${psd_s${EXTEN:2}}foo" = "foo") { Answer; Wait(1); Playback(loligo/speed-dial); SayDigits(${EXTEN:2}); Playback(loligo/is-not-set); Hangup; }; &stdexten(${psd_s${EXTEN:2}}); Congestion(10); Hangup; }; }; context app-helpdesk { *4357 => { &stdexten(41950); Congestion; }; }; context app-conferences { // waiting for room number announcement *86 => goto app-conf-hidden|s|1; }; context app-conf-hidden { s => { Wait(1); Playback(loligo/please-enter-the); Playback(loligo/extension); read(roomtoenter,loligo/then-press-pound); Meetme(${roomtoenter}); Waitexten(8); Hangup; }; _1. => Meetme(${EXTEN}); }; /////////////////////////////////////////////////////////////////////////////// // Extensions pulled from vm.conf: context vm-include { includes { vm-direct; vm-extension; vm-directory; }; }; context vm-direct { s => { Dial(SIP/5555@svm1.shsu.edu,20); Playback(im-sorry); Playback(voice-mail-system); Playback(down); Playback(extra/pls-try-call-later); Congestion(10); Hangup; }; }; context vm-extension { s => { Dial(SIP/62100@svm1.shsu.edu,20); Playback(im-sorry); Playback(voice-mail-system); Playback(down); Playback(extra/pls-try-call-later); Congestion(10); Hangup; }; }; context vm-directory { 5556 => { Dial(SIP/5556@svm1.shsu.edu); Playback(im-sorry); Playback(voice-mail-system); Playback(down); Playback(extra/pls-try-call-later); Congestion(10); Hangup; }; }; asterisk-1.4.21.2/pbx/ael/ael-test/ref.ael-ntest90000644000175000017500000000423710511011024020550 0ustar maniacmaniac (If you find progress and other non-error messages irritating, you can use -q to suppress them) (You can use the -n option if you aren't interested in seeing all the instructions generated by the compiler) (You can use the -w option to dump extensions.conf format to extensions.conf.aeldump) LOG: lev:2 file:pbx_ael.c line:3803 func: pbx_load_module Starting AEL load process. LOG: lev:2 file:pbx_ael.c line:3810 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:3818 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:3821 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. Executed ast_context_create(conts, name=workext, registrar=pbx_ael); Executed ast_context_add_ignorepat2(con, value=8, registrar=pbx_ael); Executed ast_context_add_ignorepat2(con, value=9, registrar=pbx_ael); Executed ast_add_extension2(context=workext, rep=0, exten=793, priority=1, label=(null), callerid=(null), appl=Set, data=QUERYSTRING=SELECT\ foo\,\ bar\ FROM\ foobar, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=workext, rep=0, exten=793, priority=2, label=(null), callerid=(null), appl=Verbose, data=2|${QUERYSTRING}, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=workext, rep=0, exten=793, priority=3, label=(null), callerid=(null), appl=Set, data=query=$["SELECT foo\, bar FROM foobar" ], FREE, registrar=pbx_ael); Executed ast_add_extension2(context=workext, rep=0, exten=793, priority=4, label=(null), callerid=(null), appl=Verbose, data=2|${query}, FREE, registrar=pbx_ael); LOG: lev:2 file:pbx_ael.c line:3823 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'. Executed ast_merge_contexts_and_delete(); LOG: lev:2 file:pbx_ael.c line:3826 func: pbx_load_module AEL load process: merged config file name './extensions.ael'. Executed ast_walk_contexts(); LOG: lev:2 file:pbx_ael.c line:3829 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. LOG: lev:4 file:ael2_parse line:479 func: main 1 contexts, 1 extensions, 4 priorities asterisk-1.4.21.2/pbx/ael/ael-test/ref.ael-test200000644000175000017500000000211410616372261020456 0ustar maniacmaniac (If you find progress and other non-error messages irritating, you can use -q to suppress them) (You can use the -w option to dump extensions.conf format to extensions.conf.aeldump) LOG: lev:2 file:pbx_ael.c line:3915 func: pbx_load_module Starting AEL load process. LOG: lev:2 file:pbx_ael.c line:3922 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:3930 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:3933 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:3935 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:3938 func: pbx_load_module AEL load process: merged config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:3941 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. LOG: lev:4 file:ael2_parse line:512 func: main 1 contexts, 1 extensions, 1 priorities asterisk-1.4.21.2/pbx/ael/ael-test/runtests0000755000175000017500000000241310466233565017725 0ustar maniacmaniac#!/bin/bash ORIG=`mktemp /tmp/mytest.XXXXXX` NEW=`mktemp /tmp/mytest.XXXXXX` do_filter() { sed 's/line:[0-9]*//; /^Executed.*/d; s/column=[0-9]*/ /; s/Cols: [0-9]*-[0-9]*/___/' } for i in ael-test*; do echo -n Test: $i.................. (cd $i; ../../../../utils/aelparse -n -d | grep -v -i 'seconds' > ../res.$i) do_filter < res.$i > $NEW do_filter < ref.$i > $ORIG if (diff -q $NEW $ORIG > /dev/null 2>&1 ) then echo PASSED rm res.$i else echo %%%%%%FAILED%%%%%% # diff -u ref.$i res.$i diff -u $ORIG $NEW fi done for i in ael-ntest*; do echo -n Test: $i................. (cd $i; ../../../../utils/aelparse -d | grep -v -i 'seconds' > ../res.$i) do_filter < res.$i > $NEW do_filter < ref.$i > $ORIG if (diff -q $NEW $ORIG > /dev/null 2>&1 ) then echo PASSED rm res.$i else echo %%%%%%FAILED%%%%%% # diff -u ref.$i res.$i diff -u $ORIG $NEW fi done for i in ael-vtest*; do echo -n Test: $i................. (cd $i; ../../../../utils/aelparse -d -w -n | grep -v -i 'seconds' > ../res2.$i) if (diff -q ref.$i $i/extensions.conf.aeldump > /dev/null 2>&1 ) then echo PASSED rm res2.$i rm $i/extensions.conf.aeldump else echo %%%%%%FAILED%%%%%% # diff -u ref.$i res.$i diff -u ref.$i $i/extensions.conf.aeldump fi done rm $NEW $ORIG asterisk-1.4.21.2/pbx/ael/ael-test/ref.ael-test60000644000175000017500000000364410663555422020417 0ustar maniacmaniac (If you find progress and other non-error messages irritating, you can use -q to suppress them) (You can use the -w option to dump extensions.conf format to extensions.conf.aeldump) LOG: lev:2 file:pbx_ael.c line:3978 func: pbx_load_module Starting AEL load process. LOG: lev:2 file:pbx_ael.c line:3985 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. LOG: lev:4 file:ael.flex line:276 func: ael_yylex File=./extensions.ael, line=165, column=49: Mismatched '}' in expression! LOG: lev:4 file:ael.y line:755 func: ael_yyerror ==== File: ./extensions.ael, Line 165, Cols: 51-51: Error: syntax error, unexpected '=', expecting ')' LOG: lev:4 file:ael.flex line:276 func: ael_yylex File=./extensions.ael, line=174, column=49: Mismatched '}' in expression! LOG: lev:4 file:ael.y line:755 func: ael_yyerror ==== File: ./extensions.ael, Line 174, Cols: 51-51: Error: syntax error, unexpected '=', expecting ')' LOG: lev:4 file:ael.flex line:276 func: ael_yylex File=./extensions.ael, line=222, column=41: Mismatched '}' in expression! LOG: lev:4 file:ael.y line:755 func: ael_yyerror ==== File: ./extensions.ael, Line 222, Cols: 43-43: Error: syntax error, unexpected '=', expecting ')' LOG: lev:4 file:ael.y line:755 func: ael_yyerror ==== File: ./extensions.ael, Line 291, Cols: 21-28: Error: syntax error, unexpected word, expecting '(' or ';' or '=' or ':' LOG: lev:4 file:ael.y line:755 func: ael_yyerror ==== File: ./extensions.ael, Line 291, Cols: 32-32: Error: syntax error, unexpected '|', expecting '(' or ';' or '=' or ':' LOG: lev:2 file:pbx_ael.c line:3993 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. LOG: lev:4 file:pbx_ael.c line:4006 func: pbx_load_module Sorry, but 5 syntax errors and 0 semantic errors were detected. It doesn't make sense to compile. LOG: lev:4 file:ael2_parse line:523 func: main 0 contexts, 0 extensions, 0 priorities asterisk-1.4.21.2/pbx/ael/ael-test/ref.ael-test20000644000175000017500000000533210677554425020416 0ustar maniacmaniac (If you find progress and other non-error messages irritating, you can use -q to suppress them) (You can use the -w option to dump extensions.conf format to extensions.conf.aeldump) LOG: lev:2 file:pbx_ael.c line:4048 func: pbx_load_module Starting AEL load process. LOG: lev:2 file:pbx_ael.c line:4055 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. LOG: lev:2 file:ael.flex line:654 func: setup_filestack --Read in included file ./apptest.ael2, 3474 chars LOG: lev:3 file:ael.y line:529 func: ael_yyparse ==== File: ./apptest.ael2, Line 46, Cols: 8-11: Suggestion: Use the goto statement instead of the Goto() application call in AEL. LOG: lev:2 file:pbx_ael.c line:4063 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. LOG: lev:3 file:pbx_ael.c line:2392 func: check_pval_item Warning: file ./apptest.ael2, line 35-35: application call to EndWhile needs to be re-written using AEL if, while, goto, etc. keywords instead! LOG: lev:3 file:pbx_ael.c line:2392 func: check_pval_item Warning: file ./apptest.ael2, line 37-37: application call to ExecIf needs to be re-written using AEL if, while, goto, etc. keywords instead! LOG: lev:3 file:pbx_ael.c line:1287 func: check_goto Warning: file ./apptest.ael2, line 46-46: goto: no context cont could be found that matches the goto target! LOG: lev:3 file:pbx_ael.c line:2392 func: check_pval_item Warning: file ./apptest.ael2, line 47-47: application call to GotoIf needs to be re-written using AEL if, while, goto, etc. keywords instead! LOG: lev:3 file:pbx_ael.c line:2392 func: check_pval_item Warning: file ./apptest.ael2, line 48-48: application call to GotoIfTime needs to be re-written using AEL if, while, goto, etc. keywords instead! LOG: lev:3 file:pbx_ael.c line:2392 func: check_pval_item Warning: file ./apptest.ael2, line 85-85: application call to Random needs to be re-written using AEL if, while, goto, etc. keywords instead! LOG: lev:3 file:pbx_ael.c line:2392 func: check_pval_item Warning: file ./apptest.ael2, line 141-141: application call to While needs to be re-written using AEL if, while, goto, etc. keywords instead! LOG: lev:2 file:pbx_ael.c line:4066 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:4068 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:4071 func: pbx_load_module AEL load process: merged config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:4074 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. LOG: lev:4 file:ael2_parse line:527 func: main 1 contexts, 1 extensions, 142 priorities asterisk-1.4.21.2/pbx/ael/ael-test/ref.ael-test150000644000175000017500000000211411021261300020440 0ustar maniacmaniac (If you find progress and other non-error messages irritating, you can use -q to suppress them) (You can use the -w option to dump extensions.conf format to extensions.conf.aeldump) LOG: lev:2 file:pbx_ael.c line:4131 func: pbx_load_module Starting AEL load process. LOG: lev:2 file:pbx_ael.c line:4138 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:4146 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:4149 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:4151 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:4154 func: pbx_load_module AEL load process: merged config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:4157 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. LOG: lev:4 file:ael2_parse line:543 func: main 1 contexts, 0 extensions, 0 priorities asterisk-1.4.21.2/pbx/ael/ael-test/ael-test4/0000755000175000017500000000000011041455740017701 5ustar maniacmaniacasterisk-1.4.21.2/pbx/ael/ael-test/ael-test4/apptest.ael20000644000175000017500000000662210423206707022134 0ustar maniacmaniac// this is a quick test to see how many of the apps we can spot j options in // include this in a macro or extension // at this moment, there are 18 apps that accept the j option. AddQueueMember(zork,iface,20,j); ADSIProg(sfile); AgentCallbackLogin(agent,s,30@cont); AgentLogin(agent,s); AgentMonitorOutgoing(dcn); AGI(whatever); AlarmReceiver(); Answer(2); AppendCDRUserField(value); Authenticate(pword,adjmr); BackGround(filename,snm,eng); BackgroundDetect(filename,20,2,10); Busy(10); ChangeMonitor(fnamebase); ChanIsAvail(Zap/5,sj); ChanSpy(prefix,bg()qrv); Congestion(5); ControlPlayback(filename,10,6,4,0,5,7,j); DateTime(unixtime,tz,fmt); DBdel(fam/key); DBdeltree(fam); DeadAGI(command); Dial(zap/1,45,A()CdD()fgG()hHjL()m()M()nNoprS()tTwW); Dictate(basedir); Directory(cont,dcont,f); DISA(68986869876,context); DumpChan(verblev); DUNDiLookup(90709780978,context,bj); EAGI(command); Echo(); EndWhile(); Exec(appname,args); ExecIf(expr,app,data); ExecIfTime(*,*,*,*,appname); ExternalIVR(command,arg1); Festival(text); Flash(); ForkCDR(v); GetCPEID(); Gosub(cont,exten,priority); GosubIf(cond?label); Goto(cont,exten,prior); GotoIf(cond?t:f); GotoIfTime(*,*,*,*?cont,ext,prior); Hangup(); HasNewVoicemail(vmbox,var,j); HasVoicemail(vmbox,var,j); IAX2Provision(template); ICES(xmlconfig); ImportVar(nevar@chann,var); Log(NOTICE,message); LookupBlacklist(j); LookupCIDName(); Macro(macro,arg1); MacroExit(); MacroIf(expr?etc); MailboxExists(mbox@cont,j); Math(v,2+2); MeetMe(5555,aAbcdDeimMpPqrstTovwxX); MeetMeAdmin(5555,e,user); MeetMeCount(5555,var); Milliwatt(); MixMonitor(filename,abv()V()W(),command); Monitor(file.fmt,base,mb); MP3Player(location); MusicOnHold(class); NBScat(); NoCDR(); NoOp(ignored); Page(Zap/1,dq); Park(exten); ParkAndAnnounce(template,5,238,retcont); ParkedCall(exten); PauseQueueMember(queue,zap,j); Pickup(ext@cont); Playback(file,j); PlayTones(arg); PrivacyManager(3,4,j); Progress(); Queue(queuename,dhHnrtTwW,http://www.where.what,over,5); Random(30,cont,ext,pri); Read(var,fname,10,skip,2,5); ReadFile(var=file,10); RealTime(fam,2,val,prefix); RealTimeUpdate(fam,2,val,2,newval); Record(file,2,10,anqst); RemoveQueueMember(queuename,iface,j); ResetCDR(wav); RetryDial(annound,4,2); Return(); Ringing(); RxFAX(fname,caller); SayAlpha(string); SayDigits(string); SayNumber(digits); SayPhonetic(string); SayUnixTime(unixtime,tz,fmt); SendDTMF(digits,10); SendImage(filename); SendText(text,j); SendURL(URL); Set(a=b); SetAMAFlags(); SetCallerID(clid,a); SetCallerPres(allowed_passed_screen); SetCDRUserField(value); SetGlobalVar(var=val); SetMusicOnHold(class); SetTransferCapability(SPEECH); SIPAddHeader(header); SIPDtmfMode(inband,info,rfc); SIPGetHeader(var@headername); SMS(name); SoftHangup(zap/1,a); StackPop(); StartMusicOnHold(class); StopMonitor(); StopMusicOnHold(); StopPlayTones(); System(command); TestClient(testid); TestServer(); Transfer(zap/1,j); TrySystem(command); TxFAX(filename,caller,debug); UnpauseQueueMember(queuename,iface,j); UserEvent(eventanme,body); Verbose(5,message); VMAuthenticate(mailbox@cont,s); VoiceMail(mailbox@cont,bg()suj); VoiceMailMain(mailbox@cont,pg()s); Wait(2); WaitExten(3,m()); WaitForRing(2); WaitForSilence(2,y); WaitMusicOnHold(2); While(expr); Zapateller(answer,5); ZapBarge(channel); ZapRAS(arg); ZapScan(group); ZapSendKeypadFacility(); asterisk-1.4.21.2/pbx/ael/ael-test/ael-test4/extensions.ael0000644000175000017500000000007710423206707022567 0ustar maniacmaniaccontext test1 { test2 => { #include "apptest.ael2"; } } asterisk-1.4.21.2/pbx/ael/ael-test/ref.ael-vtest170000644000175000017500000000373211021261300020637 0ustar maniacmaniac [dialextens] exten => _10X,1,Dial(Zap/${EXTEN:2}|30|tw) exten => _1ZX,1,Dial(Zap/${EXTEN:1}|30|tw) [dialthrus] exten => _3XX,1,Dial(Zap/${EXTEN:1}|30|tw) [t1incoming] include => dialextens include => parkedcalls exten => s,1,Answer() exten => s,2,Background(welcome-to-test-machine) [incoming] include => dialextens include => parkedcalls exten => s,1,Answer() exten => s,2,Background(welcome-to-test-machine) [extension] include => dialextens include => dialthrus exten => 5,1,Record(recording:gsm) exten => 5,2,Background(recording) exten => 81,1,Set(iterations=$[1000000]) exten => 81,2,Set(time1=${EPOCH}) exten => 81,3,Set(i=$[1]) exten => 81,4,GotoIf($[${i}<${iterations}]?5:8) exten => 81,5,NoOp(Hello) exten => 81,6,Set(i=$[${i}+1]) exten => 81,7,Goto(4) exten => 81,8,NoOp(Finish for-extension-1) exten => 81,9,Set(time2=${EPOCH}) exten => 81,10,Verbose(The time diff is $[${time2} - ${time1} ] seconds) exten => 81,11,Verbose(Which means that the priorities/sec = $[4* ${iterations} / (${time2} - ${time1}) ]) exten => 81,12,SayNumber($[4 * ${iterations} / (${time2} - ${time1}) ]) exten => 82,1,Macro(ndeep|100000) exten => 82,2,Verbose(Finished 100000 levels deep call!) exten => 83,1,Set(~~EXTEN~~=${EXTEN}) exten => 83,2,Goto(sw-2-${~~EXTEN~~}|10) exten => 83,3,NoOp(Finish switch-extension-2) exten => _sw-2-.,10,Goto(83|3) exten => sw-2-,10,Goto(sw-2-.|10) exten => _sw-2-[4-7]X,10,Verbose(and this too!) exten => _sw-2-[4-7]X,11,Goto(sw-2-.|10) exten => _sw-2-9X,10,Verbose(handle both 8x and 9x calls) exten => _sw-2-9X,11,Goto(sw-2-49|10) exten => _sw-2-8X,10,Verbose(do something to prepare it) exten => _sw-2-8X,11,Goto(sw-2-99|10) [macro-ndeep] exten => s,1,Set(level=${ARG1}) exten => s,2,GotoIf($[${level} == 0]?3:5) exten => s,3,Verbose(2|Got to Level 0) exten => s,4,Goto(8) exten => s,5,NoOp(Finish if-ndeep-3) exten => s,6,Macro(ndeep|$[${level}-1]) exten => s,7,Goto(8) exten => s,8,NoOp(End of Macro ndeep-s) [t1extension] include => dialextens include => dialthrus asterisk-1.4.21.2/pbx/ael/ael-test/ref.ael-ntest230000644000175000017500000000442410767661433020656 0ustar maniacmaniac (If you find progress and other non-error messages irritating, you can use -q to suppress them) (You can use the -n option if you aren't interested in seeing all the instructions generated by the compiler) (You can use the -w option to dump extensions.conf format to extensions.conf.aeldump) LOG: lev:2 file:pbx_ael.c line:4094 func: pbx_load_module Starting AEL load process. LOG: lev:2 file:pbx_ael.c line:4101 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. LOG: lev:2 file:ael.flex line:663 func: setup_filestack --Read in included file ./t1/a.ael, 41 chars LOG: lev:2 file:ael.flex line:663 func: setup_filestack --Read in included file ./t1/b.ael, 42 chars LOG: lev:2 file:ael.flex line:663 func: setup_filestack --Read in included file ./t1/c.ael, 110 chars LOG: lev:4 file:ael.y line:756 func: ael_yyerror ==== File: ./t1/c.ael, Line 3, Cols: 10-10: Error: syntax error, unexpected '(', expecting '{' LOG: lev:2 file:ael.flex line:663 func: setup_filestack --Read in included file ./t2/d.ael, 41 chars LOG: lev:2 file:ael.flex line:663 func: setup_filestack --Read in included file ./t2/e.ael, 42 chars LOG: lev:2 file:ael.flex line:663 func: setup_filestack --Read in included file ./t2/f.ael, 82 chars LOG: lev:2 file:ael.flex line:663 func: setup_filestack --Read in included file ./qq.ael, 45 chars LOG: lev:2 file:ael.flex line:663 func: setup_filestack --Read in included file ./t3/g.ael, 41 chars LOG: lev:2 file:ael.flex line:663 func: setup_filestack --Read in included file ./t3/h.ael, 42 chars LOG: lev:2 file:ael.flex line:663 func: setup_filestack --Read in included file ./t3/i.ael, 41 chars LOG: lev:2 file:ael.flex line:663 func: setup_filestack --Read in included file ./t3/j.ael, 43 chars LOG: lev:4 file:ael.y line:756 func: ael_yyerror ==== File: ./t1/c.ael, Line 10, Cols: 10-10: Error: syntax error, unexpected '(', expecting '{' LOG: lev:2 file:pbx_ael.c line:4109 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. LOG: lev:4 file:pbx_ael.c line:4122 func: pbx_load_module Sorry, but 2 syntax errors and 0 semantic errors were detected. It doesn't make sense to compile. LOG: lev:4 file:ael2_parse line:543 func: main 0 contexts, 0 extensions, 0 priorities asterisk-1.4.21.2/pbx/ael/ael-test/ref.ael-test50000644000175000017500000000212011021261300020354 0ustar maniacmaniac (If you find progress and other non-error messages irritating, you can use -q to suppress them) (You can use the -w option to dump extensions.conf format to extensions.conf.aeldump) LOG: lev:2 file:pbx_ael.c line:4131 func: pbx_load_module Starting AEL load process. LOG: lev:2 file:pbx_ael.c line:4138 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:4146 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:4149 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:4151 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:4154 func: pbx_load_module AEL load process: merged config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:4157 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. LOG: lev:4 file:ael2_parse line:543 func: main 38 contexts, 91 extensions, 486 priorities asterisk-1.4.21.2/pbx/ael/ael-test/ael-test6/0000755000175000017500000000000011041455740017703 5ustar maniacmaniacasterisk-1.4.21.2/pbx/ael/ael-test/ael-test6/extensions.ael0000644000175000017500000004351510423206707022575 0ustar maniacmaniac/////////////////////////////////////////////////////////////////////////////// // Helpdesk Queue context hd-queue { s => { NoOp(Add a background sound to tell the user their options); Queue(helpdesk|t); NoOp(Put in options to apologize and send user to voicemail); }; 0 => goto default|0|1; 1 => { Dial(u41950@svm1.shsu.edu); Congestion(10); Hangup; }; }; context l903-calling { _9903NXXXXXX => { Realtime(l903_ext|exchange|${EXTEN:4:3}|l903_); if ("${l903_exchange}foo" = "foo") { Playback(num-outside-area); SayDigits(1); Playback(and-area-code); Playback(before-the-number); Hangup; }; &dialout(${EXTEN}); Congestion(10); Hangup; }; }; /////////////////////////////////////////////////////////////////////////////// // Extensions pulled from houston.conf // Converted the extension list to the database context houston-calling { _9713NXXXXXX => { Realtime(hou_713_ext|exchange|${EXTEN:4:3}|hou_713_); if ("${hou_713_exchange}foo" = "foo") { Playback(num-outside-area); SayDigits(1); Playback(and-area-code); Playback(before-the-number); Hangup; }; &dialout(${EXTEN}); Congestion(10); Hangup; }; _9281NXXXXXX => { Realtime(hou_281_ext|exchange|${EXTEN:4:3}|hou_281_); if ("${hou_281_exchange}foo" = "foo") { Playback(num-outside-area); SayDigits(1); Playback(and-area-code); Playback(before-the-number); Hangup; }; &dialout(${EXTEN}); Congestion(10); Hangup; }; _9832NXXXXXX => { Realtime(hou_832_ext|exchange|${EXTEN:4:3}|hou_832_); if ("${hou_832_exchange}foo" = "foo") { Playback(num-outside-area); SayDigits(1); Playback(and-area-code); Playback(before-the-number); Hangup; }; &dialout(${EXTEN}); Congestion(10); Hangup; }; }; /////////////////////////////////////////////////////////////////////////////// // Extensions pulled from huntsville.conf // Converted the extension list to the database context huntsville-calling { _9NXXXXXX => { Realtime(hv_ext|exchange|${EXTEN:1:3}|hv_); if ("${hv_exchange}foo" = "foo") { Playback(num-outside-area); SayDigits(1); Playback(and-area-code); Playback(before-the-number); Hangup; }; &dialout(${EXTEN}); Congestion(10); Hangup; }; _NXXXXXX => { NoOp(Stripping last four to see what extension we're dialing); Set(LAST4=${EXTEN:3}); StripLSD(4); }; i => Playback(pbx-invalid); h => Hangup; }; /////////////////////////////////////////////////////////////////////////////// // Extensions pulled from macros.conf macro dialout( number ) { Realtime(call_info|exten|${CALLERIDNUM:5}|mon_); if ("${mon_monitor}" = "YES") { Dial(SIP/${number}@sgw1.shsu.edu,,wW); Dial(SIP/${number}@sgw2.shsu.edu,,wW); } else { Dial(SIP/${number}@sgw1.shsu.edu); Dial(SIP/${number}@sgw2.shsu.edu); }; }; // Standard extension macro: // ${ext} - Extension macro stdexten( ext ) { Realtime(sipusers|name|${ext}|sip_user_); Realtime(call_info|exten|${ext}|info_); if ("${sip_user_name}foo" = "foo") { Wait(1); &dialout(${ext}); Congestion(10); Hangup; }; NoOp(${CALLERIDNUM}); RealtimeUpdate(call_info|exten|${ext}|calltrace|${CALLERIDNUM}); System(/usr/local/bin/db_update.sh call_info calltrace ${CALLERIDNUM} exten ${ext} &); &checkdnd(${ext}); &checkcf(${ext}); Realtime(call_info|exten|${CALLERIDNUM:5}|mon_); if ("${mon_monitor}" = "YES") { Dial(SIP/${info_forwardto},25,wW); } else { Dial(SIP/${info_forwardto},25); }; switch ("${DIALSTATUS}") { case "BUSY": &checkcfb(${ext}); break; case "CHANUNAVAIL": Dial(IAX2/asterisk:password@scm2.shsu.edu/${info_forwardto},25,wW); MailboxExists(${ext}); // if ("${VMBOXEXISTSSTATUS}" = "FAILED") { // Congestion(10); // Hangup; // }; &uvm(${ext}); Hangup; break; case "CONGESTION": MailboxExists(${ext}); if ("$(VMBOXEXISTSSTATUS}" = "FAILED") { Congestion(10); Hangup; }; &bvm(${ext}); Hangup; break; default: MailboxExists(${ext}); if ("$(VMBOXEXISTSSTATUS}" = "FAILED") { Congestion(10); Hangup; }; &uvm(${ext}); Hangup; }; Hangup; }; macro uvm( ext ) { Dial(SIP/u${ext}@svm1.shsu.edu); Playback(im-sorry); Playback(voice-mail-system); Playback(down); Congestion(10); Hangup; }; macro bvm( ext ) { Dial(SIP/b${ext}@svm1.shsu.edu); Playback(im-sorry); Playback(voice-mail-system); Playback(down); Congestion(10); Hangup; }; macro checkdnd( ext ) { if ("${info_donotdisturb}foo" = "foo") { NoOp(Do Not Disturb is not active); } else &uvm(${ext}); }; macro checkcf( ext ) { if ("${info_forwardto}foo" = "foo") if ("${ext}" = "43974") { Set(info_forwardto=${ext}&SCCP/${ext}); } else { Set(info_forwardto=${ext}&SIP/${ext}w); }; }; macro checkcfb( ext ) { if ("${info_forwardbusy}foo" = "foo") { Wait(1); MailboxExists(${ext}); if ("$(VMBOXEXISTSSTATUS}" = "FAILED") { &dialout(${ext}); Hangup; }; &bvm(${ext}); Hangup; }; &stdexten(${info_forwardbusy}); }; /////////////////////////////////////////////////////////////////////////////// // Extensions pulled from test.conf context test-include { includes { test-digium; test-sounds; test-phinfo; }; }; context test-digium { *500 => { Dial(IAX2/guest@misery.digium.com/s@default); Playback(demo-nogo); Hangup; }; }; context test-sounds { *501 => { Answer; Musiconhold; Wait(1); Hangup; }; }; context test-phinfo { *505 => { Answer; NoOp(${CALLERIDNUM:5}); SayDigits(${CALLERIDNUM:5}); Hangup; }; }; /////////////////////////////////////////////////////////////////////////////// // Extensions pulled from external.conf context long-distance { includes { local; }; _91XXXXXXXXXX => &dialout(${EXTEN}); _9011. => &dialout(${EXTEN}); }; context local { includes { default; }; 911 => &dialout(911); 9911 => &dialout(9911); _9NXXXXXX => goto huntsville-calling|${EXTEN}|1; _936NXXXXXX => { Goto 9${EXTEN:3}|1; Congestion(10); Hangup; }; _832NXXXXXX => { goto 9${EXTEN}|1; Congestion(10); Hangup; }; _713NXXXXXX => { goto 9${EXTEN}|1 ; Congestion(10); Hangup; }; _281NXXXXXX => { goto 9${EXTEN}|1; Congestion(10); Hangup; }; _NXXNXXXXXX => { goto 9${EXTEN}|1; goto 91${EXTEN}|1; Congestion(10); Hangup; }; _91800NXXXXXX => &dialout(${EXTEN}); _91866NXXXXXX => &dialout(${EXTEN}); _91877NXXXXXX => &dialout(${EXTEN}); _91888NXXXXXX => &dialout(${EXTEN}); _91900NXXXXXX => &dialout(${EXTEN}); _91976NXXXXXX => &dialout(${EXTEN}); _9713NXXXXXX => goto houston-calling|${EXTEN}|1; _9281NXXXXXX => goto houston-calling|${EXTEN}|1; _9832NXXXXXX => goto houston-calling|${EXTEN}|1; _9903NXXXXXX => goto l903-calling|${EXTEN}|1; _31NXXNXXXXXX => &dialout(${EXTEN}); h => Hangup; }; /////////////////////////////////////////////////////////////////////////////// // Extensions pulled from internal.conf context from-scm2 { _4XXXX => { NoOp(DIALING SIP EXTENSION ${EXTEN} - FROM ${CALLERIDNUM}); Dial(SIP/${EXTEN},20,wW); Hangup; }; _6XXXX => { NoOp(DIALING SIP EXTENSION ${EXTEN} - FROM ${CALLERIDNUM}); Dial(SIP/${EXTEN},20,wW); Hangup; }; }; /////////////////////////////////////////////////////////// // All internal extensions work through the default context // Phones that can only make internal calls should be in // this context. /////////////////////////////////////////////////////////// context default { // Include the contexts in the files that allow us to make these phone calls includes { vm-include; apps-include; test-include; }; // ALWAYS have an 'h' extension h => { NoOp(Hangup cause was: ${HANGUPCAUSE}); Hangup; }; // We like to hear that we dialed an invalid extension i => Playback(pbx-invalid); // Dial the operator 0 => &dialout(0); // Send voicemail calls to the vm-* contexts to be handled voicemail => goto vm-direct|s|1; 5555 => goto vm-direct|s|1; 62100 => goto vm-extension|s|1; // These are our campus extensions, send them to the macro _6XXXX => &stdexten(${EXTEN}); _4XXXX => &stdexten(${EXTEN}); // These are campus extensions as well, might need to take this out though. _9294XXXX => goto _4XXXX|1; _9496XXXX => goto _6XXXX|1; // These allows us to dial from the directory in our phone without worrying about dialing 9 _936294XXXX => { goto ${EXTEN:5}|1; goto 9${EXTEN:3}|1; Congestion(10); Hangup; }; _936496XXXX => { goto ${EXTEN:5}|1; goto 9${EXTEN:3}|1; Congestion(10); Hangup; }; }; /////////////////////////////////////////////////////////////////////////////// // Extensions pulled from apps.conf context apps-include { includes { app-agents; app-dnd; app-callforward; app-calltrace; app-conferences; app-ssd; app-psd; app-idblock; app-helpdesk; app-dictate; app-set-monitor; }; }; context app-agents { *54 => { Answer; Wait(1); Read(agent_no|agent-user); AgentCallbackLogin(${agent_no}|s${CALLERIDNUM:5}); Playback(agent-loginok); Hangup; }; *55 => { Answer; Wait(1); AgentCallbackLogin(${agent_no}); Hangup; }; }; context app-calltrace { // caller dials this to find out the last call missed and possibly call back *69 => goto app-calltrace-perform|s|1; }; context app-calltrace-perform { s => { Answer; Wait(1); Background(info-about-last-call); Background(telephone-number); RealTime(call_info|exten|${CALLERIDNUM:5}|ct_); if ("${ct_calltrace}foo" = "foo") { Playback(loligo/from-unknown-caller); Hangup; } else { SayDigits("${ct_calltrace}"); Set(TIMEOUT(digit)=3); Set(TIMEOUT(response)=7); Background(loligo/to-call-this-number); Background(press-1); Background(loligo/silence/5); }; }; 1 => goto local|${ct_calltrace}|1; i => { Playback(vm-goodbye); Hangup; }; t => { Playback(vm-goodbye); Hangup; }; }; context app-set-monitor { *50 => { Realtime(call_info|exten|${CALLERIDNUM:5}|mon_set_); if ("${mon_set_monitor}" = "YES") { RealtimeUpdate(call_info|exten|${CALLERIDNUM:5}|monitor|); System(/usr/local/bin/db_update.sh call_info monitor '' exten ${CALLERIDNUM:5} &); } else { RealtimeUpdate(call_info|exten|${CALLERIDNUM:5}|monitor|YES); System(/usr/local/bin/db_update.sh call_info monitor YES exten ${CALLERIDNUM:5} &); }; NoOp(${mon_set_monitor}); Hangup; }; }; context app-dnd { *78 => { Answer; Wait(1); RealtimeUpdate(call_info|exten|${CALLERIDNUM:5}|donotdisturb|YES); System(/usr/local/bin/db_update.sh call_info donotdisturb YES exten ${CALLERIDNUM:5} &); Playback(do-not-disturb); Playback(loligo/activated); Hangup; }; *79 => { Answer; Wait(1); RealtimeUpdate(call_info|exten|${CALLERIDNUM:5}|donotdisturb|); System(/usr/local/bin/db_update.sh call_info donotdisturb '' exten ${CALLERIDNUM:5} &); Playback(do-not-disturb); Playback(loligo/de-activated); Hangup; }; }; context app-callforward { // forwards calling extension to input number *72{EXTEN} _*72. => { RealtimeUpdate(call_info|exten|${CALLERIDNUM:5}|forwardto|${EXTEN:3}); System(/usr/local/bin/db_update.sh call_info forwardto ${EXTEN:3} exten ${CALLERIDNUM:5} &); Answer; Wait(1); Playback(loligo/call-fwd-unconditional); Playback(loligo/for); Playback(loligo/extension); SayDigits(${CALLERIDNUM:5}); Playback(loligo/is-set-to); SayDigits(${EXTEN:3}); Hangup; }; // prompts for extension to forward to *72 => { Answer; Wait(1); Playback(please-enter-your); Playback(extension); Background(then-press-pound); VMAuthenticate(|s); Background(loligo/ent-target-attendant); Read(toext,loligo/then-press-pound); Wait(1); RealtimeUpdate(call_info|exten|${AUTH_MAILBOX}|forwardto|${toext}); System(/usr/local/bin/db_update.sh call_info forwardto ${toext} exten ${AUTH_MAILBOX} &); Playback(loligo/call-fwd-unconditional); Playback(loligo/for); Playback(loligo/extension); SayDigits(${AUTH_MAILBOX}); Playback(loligo/is-set-to); SayDigits(${toext}); Hangup; }; // cancels dialed extension call forward _*73. => { Realtime(voicemail|mailbox|${EXTEN:3}|auth_); Answer; Wait(1); Authenticate(${auth_password}); RealtimeUpdate(call_info|exten|${EXTEN:3}|forwardto|); System(/usr/local/bin/db_update.sh call_info forwardto '' exten ${EXTEN:3} &); Wait(1); SayDigits(${EXTEN:3}); Playback(loligo/call-fwd-cancelled); Hangup; }; // cancels call forward for calling extension *73 => { RealtimeUpdate(call_info|exten|${CALLERIDNUM:5}|forwardto|); System(/usr/local/bin/db_update.sh call_info forwardto '' exten ${CALLERIDNUM:5} &); Answer; Wait(1); Playback(loligo/call-fwd-cancelled); Hangup; }; // dialed call forward on busy _*90. => { RealtimeUpdate(call_info|exten|${CALLERIDNUM:5}|forwardbusy|${EXTEN:3}); System(/usr/local/bin/db_update.sh call_info forwardbusy ${EXTEN:3} exten ${CALLERIDNUM:5} &); Answer; Wait(1); Playback(loligo/call-fwd-on-busy); Playback(loligo/for); Playback(loligo/extension); SayDigits(${CALLERIDNUM:5}); Playback(loligo/is-set-to); SayDigits(${EXTEN:3}); Hangup; }; // cancels call forward on busy for calling extension *91 => { RealtimeUpdate(call_info|exten|${CALLERIDNUM:5}|forwardbusy|); System(/usr/local/bin/db_update.sh call_info forwardbusy '' exten ${CALLERIDNUM:5} &); Answer; Wait(1); Playback(loligo/call-fwd-on-busy); Playback(loligo/de-activated); Hangup; }; h => Hangup; }; context app-idblock { _*67. => { Set(CALLERID(name)=Anonymous); &stdexten(${EXTEN:3}); }; }; context app-dictate { *1 => { Dictate(); Hangup; }; }; context app-ssd { // *59 - Set system speed dial to digits // *59 0 - Delete system speed dial // *59 - Review system speed dial // *1xx - Dial speed dial _*59XXX. => { Answer; RealtimeUpdate(ssd|sd|${EXTEN:3:2}|extension|${EXTEN:5}); System(/usr/local/bin/db_update.sh systemsd extension ${EXTEN:5} sd ${EXTEN:3:2} &); Wait(1); Playback(loligo/speed-dial); SayDigits(${EXTEN:3:2}); Playback(loligo/has-been-set-to); SayDigits(${EXTEN:5}); Hangup; }; _*59XX0 => { Answer; RealtimeUpdate(ssd|sd|${EXTEN:3:2}|extension|); System(/usr/local/bin/db_update.sh systemsd extension '' sd ${EXTEN:3:2} &); Wait(1); Playback(loligo/speed-dial); SayDigits(${EXTEN:3:2}); Playback(loligo/has-been-cleared); Hangup; }; _*59XX => { Answer; Realtime(ssd|sd|${EXTEN:3}|ssd_); if ("${ssd_extension}foo" = "foo") { Playback(loligo/speed-dial); SayDigits(${EXTEN:3:2}); Playback(loligo/is-not-set); Hangup; }; Wait(1); Playback(loligo/speed-dial); SayDigits(${EXTEN:3:2}); Playback(loligo/is-set-to); SayDigits(${ssd_extension}); Hangup; }; // NTC = number to call _*1XX => { Realtime(ssd|sd|${EXTEN:2}|ssd_); if ("${ssd_extension}foo" = "foo") { Answer; Wait(1); Playback(loligo/speed-dial); SayDigits(${EXTEN:2}); Playback(loligo/is-not-set); Hangup; }; &stdexten(${ssd_extension}); Congestion(10); Hangup; }; }; macro check-psd-exists ( ext ) { Realtime(psd|extension|${ext}|psd_); if ("${psd_extension}foo" = "foo") { System(/usr/local/bin/create_psd.sh ${ext}); } else NoOp(PSD set for ${ext}); }; context app-psd { // *89 - Set personal speed dial to digits // *89 0 - Delete personal speed dial // *89 - Review personal speed dial // *2xx - Dial personal speed dial _*89XXX. => { &check-psd-exists(${CALLERIDNUM:5}); Answer; RealtimeUpdate(psd|extension|${CALLERIDNUM:5}|s${EXTEN:3:2}|${EXTEN:5}); System(/usr/local/bin/db_update.sh personalsd s${EXTEN:3:2} ${EXTEN:5} extension ${CALLERIDNUM:5} &); Wait(1); Playback(loligo/speed-dial); SayDigits(${EXTEN:3:2}); Playback(loligo/has-been-set-to); SayDigits(${EXTEN:5}); Hangup; }; _*89XX0 => { &check-psd-exists(${CALLERIDNUM:5}); Answer; RealtimeUpdate(psd|extension|${CALLERIDNUM:5}|s${EXTEN:3:2}|); System(/usr/local/bin/db_update.sh personalsd s${EXTEN:3:2} '' extension ${CALLERIDNUM:5} &); Wait(1); Playback(loligo/speed-dial); SayDigits(${EXTEN:3:2}); Playback(loligo/has-been-cleared); Hangup; }; _*89XX => { &check-psd-exists(${CALLERIDNUM:5}); Answer; Realtime(psd|extension|${CALLERIDNUM:5}|psd_); Wait(1); if ("${psd_s${EXTEN:3:2}}foo" = "foo") { Playback(loligo/speed-dial); SayDigits(${EXTEN:3:2}); Playback(loligo/is-not-set); Hangup; }; Playback(loligo/speed-dial); SayDigits(${EXTEN:3:2}); Playback(loligo/is-set-to); SayDigits(${psd_s${EXTEN:3:2}}); Hangup; }; // NTC = number to call _*2XX => { &check-psd-exists(${CALLERIDNUM:5}); Realtime(psd|extension|${CALLERIDNUM:5}|psd_); if ("${psd_s${EXTEN:2}}foo" = "foo") { Answer; Wait(1); Playback(loligo/speed-dial); SayDigits(${EXTEN:2}); Playback(loligo/is-not-set); Hangup; }; &stdexten(${psd_s${EXTEN:2}}); Congestion(10); Hangup; }; }; context app-helpdesk { *4357 => { &stdexten(41950); Congestion; }; }; context app-conferences { // waiting for room number announcement *86 => goto app-conf-hidden|s|1; }; context app-conf-hidden { s => { Wait(1); Playback(loligo/please-enter-the); Playback(loligo/extension); read(roomtoenter,loligo/then-press-pound); Meetme(${roomtoenter}); Waitexten(8); Hangup; }; _1. => Meetme(${EXTEN}); }; /////////////////////////////////////////////////////////////////////////////// // Extensions pulled from vm.conf: context vm-include { includes { vm-direct; vm-extension; vm-directory; }; }; context vm-direct { s => { Dial(SIP/5555@svm1.shsu.edu,20); Playback(im-sorry); Playback(voice-mail-system); Playback(down); Playback(extra/pls-try-call-later); Congestion(10); Hangup; }; }; context vm-extension { s => { Dial(SIP/62100@svm1.shsu.edu,20); Playback(im-sorry); Playback(voice-mail-system); Playback(down); Playback(extra/pls-try-call-later); Congestion(10); Hangup; }; }; context vm-directory { 5556 => { Dial(SIP/5556@svm1.shsu.edu); Playback(im-sorry); Playback(voice-mail-system); Playback(down); Playback(extra/pls-try-call-later); Congestion(10); Hangup; }; }; asterisk-1.4.21.2/pbx/ael/ael-test/ael-test15/0000755000175000017500000000000011041455740017763 5ustar maniacmaniacasterisk-1.4.21.2/pbx/ael/ael-test/ael-test15/extensions.ael0000644000175000017500000000032510475447433022657 0ustar maniacmaniac/* and some comments would make a nice touch */ context t1 { /* this a test of block comments */ _15x => { /* more comments across several lines * what do you think* */ } } /* amd some more */ asterisk-1.4.21.2/pbx/ael/ael-test/ael-test16/0000755000175000017500000000000011041455740017764 5ustar maniacmaniacasterisk-1.4.21.2/pbx/ael/ael-test/ael-test16/extensions.ael0000644000175000017500000000003110511011024022620 0ustar maniacmaniaccontext real-small { } asterisk-1.4.21.2/pbx/ael/ael-test/ael-test11/0000755000175000017500000000000011041455740017757 5ustar maniacmaniacasterisk-1.4.21.2/pbx/ael/ael-test/ael-test11/extensions.ael0000644000175000017500000000154510465634503022653 0ustar maniacmaniaccontext test1 { s => { goto lab1; if( ${testnotnull} ) { lab1: NoOp(hello); } else { lab1: MoOp(goodbye); } } 1 => { lab1: NoOp(This one is OK.); } } macro endcall5(type) { switch(${type}) { case out: if(${testnotnull}) { NoOp(whoosh); goto ptr1 ; // <-- goto call to valid label } case in: ptr1: // The First label is the valid one... if(${testnotnull}) { NoOp(wow); goto ptr1 ; // <-- goto call to valid label } Noop(esac) ; } if(${testnotnull}) { goto ptr1; } switch(${type}) { case out: switch(${type}) { case in: if(${testnotnull}) { ptr1: // <-- duplicate label (macros are about the equiv of an extension) Softhangup(${CHANNEL}); break ; } Noop(esac) ; } } } asterisk-1.4.21.2/pbx/ael/ael-test/ael-ntest9/0000755000175000017500000000000011041455736020071 5ustar maniacmaniacasterisk-1.4.21.2/pbx/ael/ael-test/ael-ntest9/extensions.ael0000755000175000017500000000033710453311556022756 0ustar maniacmaniac context workext { ignorepat => 8; ignorepat => 9; 793 => { Set(QUERYSTRING=SELECT\ foo\,\ bar\ FROM\ foobar); Verbose(2|${QUERYSTRING}); query="SELECT foo\, bar FROM foobar" ; Verbose(2|${query}) ; } } asterisk-1.4.21.2/pbx/ael/ael-test/ael-test18/0000755000175000017500000000000011041455740017766 5ustar maniacmaniacasterisk-1.4.21.2/pbx/ael/ael-test/ael-test18/extensions.ael0000644000175000017500000000173410616372261022660 0ustar maniacmaniaccontext default { 706/3077610011 => { JabberStatus(asterisk|jmls@mike,StatusCode); switch(${StatusCode}) { case 1: Dial(SIP/706,12); switch(${DIALSTATUS}) { case BUSY: Voicemail(b706); break; default: Voicemail(u706); }; BackGround(hello); break; default: Voicemail(u706); }; ifTime(3:00-13:00|*|*|*) { NoOp(hello); label1: NoOp(goodbye); } else { NoOp(hithere); label2: NoOp(whatonearth?); } goto label1; goto label2; Hangup(); }; } asterisk-1.4.21.2/pbx/ael/ael-test/ael-test7/0000755000175000017500000000000011041455740017704 5ustar maniacmaniacasterisk-1.4.21.2/pbx/ael/ael-test/ael-test7/extensions.ael0000644000175000017500000002514110423206707022571 0ustar maniacmaniac// // Example AEL config file // globals { CONSOLE=Console/dsp; TRUNKMSD=0; //MSD digits to strip (usually 1 or 0) TRUNCPROTO=SIP; TRUNK=sunrocket; PSTN=pstn-spa3k; PSTNPROTO=SIP; TARIOPROTO=SIP; TARIO=tario; CPPROTO=SIP; CPACKET1=callpacket1; CPACKET2=callpacket2; SELLVOIP=1577040314; SVPROTO=IAX2; }; macro stdexten (ext , dev ) { PrivacyManager(3,10); if("${PRIVACYMGRSTATUS}" = "FAILED") { Playback(vm-goodbye); Hangup(); }; AGI(calleridnamelookup.agi); Dial(${dev}/${ext},30,t); switch(${DIALSTATUS}) { case BUSY: Voicemail(b${ext}); break; default: Voicemail(u${ext}); }; catch a { VoiceMailMain(${ext}); return; }; }; macro announce_minutes(minutes) { Playback(vm-youhave); SayNumber(${minutes}); Playback(vm-minutes); Wait(1); }; // Check if given provider allows only some free minutes per month // and announce number of free minutes remaining. // The limit will be reset monthly by cron job. // The macro sets the following variables: // MINUTES_LIMIT - number of free minutes per month // MINUTES_USED - number of free minutes used in the current month // PROVIDER - provider name macro checkanddial(prov,proto,ext,arg1,arg2,arg3,arg4) { Set(MINUTES_LIMIT=0); Set(MINUTES_USED=0); Set(PROVIDER=${prov}); if(${DB_EXISTS(Provider/${prov}/used)}) Set(MINUTES_USED=${DB_RESULT}); country_c = 0; switch(${LEN(${ext})}) { //assuming all international numbers are 11 digits long. case 10: //NXXNXXXXXX country_c=1; break; case 11: //XNXXNXXXXXX country_c = ${ext:0:1}; break; default: //011XNXXNXXXXXX country_c = ${ext:3:1}; break; }; if("${prov}" = "${TRUNK}" & ${country_c} != 1) { // SunRocket international calls Set(MINUTES_LIMIT=${DB(Provider/${prov}/limit)}); &announce_minutes($[${MINUTES_LIMIT} - ${MINUTES_USED}]); }; if("${prov}" = "${CPACKET1}" | "${prov}" = "${CPACKET2}") { // Callpacket has a limit on domestic calls Set(MINUTES_LIMIT=${DB(Provider/${prov}/limit)}); &announce_minutes($[${MINUTES_LIMIT} - ${MINUTES_USED}]); }; DeadAGI(dial.agi,${proto}/${ext}@${prov},${arg1},${arg2},${arg3},${arg4}); }; macro trunkdial(ext) { // Dial sunrocket and set correct collerid if("${CALLERID(number)}" = "1") { Set(CALLERID(number)=7322271653); } else { Set(CALLERID(number)=7326260100); }; Set(CALLERID(name)=Sergey Okhapkin); &checkanddial(${TRUNK},${TRUNCPROTO},${ext},60,T); Hangup; }; macro checklocal(ext) { // lookup the number in DB and call the number via pstn or sunrocket Set(AREACODE=${ext:0:3}); Set(EXCHANGE=${ext:3:3}); Set(IS_LOCAL=${DB_EXISTS(localnum/${AREACODE}/${EXCHANGE})}); if(${IS_LOCAL}) { &checkanddial(${PSTN},${PSTNPROTO},${ext},60,T); if ("${DIALSTATUS}" = "BUSY") &trunkdial(${ext}); } else &trunkdial(${ext}); }; macro autodial(ext) { // Find Least Cost Route LCDial(${ext},60,T); if("${DIALSTATUS}" = "NOPROVIDER") Playback(invalid); Hangup(); }; context default { // Calls to us s => { Wait(1); Answer; start: Set(TIMEOUT(digit)=3); Set(TIMEOUT(response)=10); repeat: for (x=0; ${x} < 5; x=${x} + 1) { Background(home/greeting); WaitExten(); }; }; t => jump *; i => { // invalid extension Playback(invalid); goto s|repeat; }; _* => { Playback(vm-goodbye); Wait(1); Hangup; }; 1 => &stdexten(1,SIP/1); 2 => &stdexten(2,SIP/2); 3 => &stdexten(3,SIP/3); 2271653 => jump 1; 7322271653 => jump 1; 17322271653 => jump 1; 6260100 => jump 2; 7326260100 => jump 2; 17326260100 => jump 2; 8058701100 => jump 2; 3103622835 => jump 2; sos => jump 2; 1400898 => jump 2; 6260101 => jump s; 7326260101 => jump s; 17326260101 => jump s; 2271677 => jump 3; 7322271677 => jump 3; 17322271677 => jump 3; galka => jump 3; 911 => Dial(${PSTNPROTO}/911@${PSTN},60,); 380 => Dial(SIP/topspeen@212.40.38.70,60,T); // Fun stuff 100 => { SayUnixTime(); goto s|start; }; 101 => { // Voicemail VoicemailMain(${CALLERID(number)}); Hangup; }; 102 => MusicOnHold(); // 103 => { // Wait(1); //start: // Read(NUMBER,vm-enter-num-to-call); // LCDial(${NUMBER},T); // goto start; // }; 105 => jump s@phrase-menu; 7312 => { ForkCDR; Set(CALLERID(name)=Sergey Okhapkin); Set(CALLERID(number)=7326260100); DISA(1111|home); }; }; context goiax { s => { Answer(); Ringing(); Wait(1); start: Read(NUMBER,vm-enter-num-to-call); Set(CALLERID(name)=Central NJ); Dial(IAX2/14301@fwdOUT/q${NUMBER},60,T); goto start; }; }; context phrase-menu { s => { Answer; // Answer the line TIMEOUT(digit)=2; // Set Digit Timeout to 5 seconds TIMEOUT(response)=10; // Set Response Timeout to 10 seconds BackGround(custom/phrase-menu); // Play main menu. }; 1 => { // Phrase Recording Wait(1); Read(PHRASEID|custom/enter-phrase-num); Wait(2); // give yourself 2 secs to take a breath and wait for beep Record(custom/${PHRASEID}:gsm); Wait(2); Playback(custom/${PHRASEID}); Wait(1); jump s; }; 2 => { // Phrase review Wait(1); Read(PHRASEID|custom/enter-phrase-num); Wait(1); Playback(custom/${PHRASEID}); Wait(1); jump s; }; t => Hangup; i => { Playback(custom/invalid-option); jump s; }; }; context outbound { // North America seven-, ten- and eleven digits _NXXXXXX => &autodial(1732${EXTEN}); _NXXNXXXXXX => &autodial(1${EXTEN}); _ZNXXNXXXXX. => &autodial(${EXTEN}); // Toll free numbers via PSTN // _1800NXXXXXX => &checkanddial(${PSTN},${PSTNPROTO},${EXTEN},60,T); // _1888NXXXXXX => &checkanddial(${PSTN},${PSTNPROTO},${EXTEN},60,T); // _1877NXXXXXX => &checkanddial(${PSTN},${PSTNPROTO},${EXTEN},60,T); // _1866NXXXXXX => &checkanddial(${PSTN},${PSTNPROTO},${EXTEN},60,T); _011. => { //International context accessed through trunk &trunkdial(${EXTEN}); }; _012. => { //fwdOUT Set(CALLERID(name)=Central NJ); Dial(IAX2/14301@fwdOUT/q${EXTEN:3},60,T); }; _013X. => { //NECC Dial(${PSTNPROTO}/011${EXTEN:3}@${PSTN},60,T); }; _0131. => { //NECC to US Dial(${PSTNPROTO}/${EXTEN:3}@${PSTN},60,T); }; _014. => { //TARIO by SIP ID Set(CALLERID(name)=Sergey Okhapkin); Set(CALLERID(number)=1400898); Dial(${TARIOPROTO}/${EXTEN:3}@${TARIO},60,T); }; _0157. => { //TARIO outbound Russia Set(CALLERID(name)=Sergey Okhapkin); Set(CALLERID(number)=1400898); Dial(${TARIOPROTO}/8${EXTEN:4}@${TARIO},60,T); }; // _015. => { //TARIO outbound international // CALLERID(name)="Sergey Okhapkin"; // CALLERID(number)=1400898; // Dial(${TARIOPROTO}/810${EXTEN:3}@${TARIO},60,T); // }; _0161NXXNXXXXXX => { //Callpacket outbound USA/Canada &checkanddial(${CPACKET1},${CPPROTO},${EXTEN:3},60,T); }; _0171NXXNXXXXXX => { //Callpacket outbound USA/Canada &checkanddial(${CPACKET2},${CPPROTO},${EXTEN:3},60,T); }; _0181NXXNXXXXXX => { //sellvoip outbound USA/Canada Dial(${SVPROTO}/${SELLVOIP}@${SELLVOIP}/${EXTEN:3},60,T); }; _019. => { //Voipbuster Dial(IAX2/sokhapkin@voipbuster/00${EXTEN:3},60,T); }; }; context home { //calls from us includes { default; outbound; }; }; context sunrocket-in { 7322271653 => jump s; 7326260100 => jump 2@default; s => { if("${CALLERID(number)}" = "sunrocketcom") Set(CALLERID(number)=); switch(${CALLERID(RDNIS)}) { case 7326260100: jump 2@default; break; case 7326260101: jump s@default; break; default: jump 1@default; break; }; }; }; context pstn-in { 3 => { if ("${CALLERID(number)}" = "7322271677") Set(CALLERID(number)=); jump 3@default; }; }; context tario.net-in { _X. => { Set(CALLERID(name)=); if("${CALLERID(number):-11:1}" = "8") Set(CALLERID(number)=7${CALLERID(number):1}); if("${SIP_HEADER(To)}" = "") { jump 3@default; } else if("${SIP_HEADER(To)}" = "") { jump 1@default; } else jump 2@default; }; }; context from-callpacket { 8058701100 => jump 2@default; 3103622835 => { Answer; Ringing; Wait(10); Voicemail(b3103622835); Hangup; }; a => Hangup; }; context fromfwdOUT { // make sure we only accept US and Canada calls, limit to 30 minutes includes { fromfwdOUT-catchbad; fromfwdOUT-isgood; fromfwdOUT-catchall; }; }; context fromfwdOUT-isgood { _17326260100 => jump 2@default; _17326260101 => jump s@default; _17322271653 => jump 1@default; _17322271677 => jump 3@default; _1NXXNXXXXXX => { Set(CALLERID(name)=Sergey Okhapkin); // Set(CALLERID(number)=7326260100); // Dial(${TRUNCPROTO}/*67${EXTEN:${TRUNKMSD}}@${TRUNK},60,,L(1800000:60000)); Dial(${CPPROTO}/${EXTEN}@${CPACKET2},60,,L(1800000:60000)); }; }; context fromfwdOUT-catchbad { //block bahamas, etc _1900. => congestion ; //N11 _1XXX976. => congestion ; //N11 _1XXX555. => congestion ; //N11 _1X11. => congestion ; //N11 _1867. => congestion ; //Yukon (sorry mike) // exten => _1NPA Country _1242. => congestion; //BAHAMAS _1246. => congestion; //BARBADOS _1264. => congestion; //ANGUILLA _1268. => congestion; //ANTIGUA/BARBUDA _1284. => congestion; //BRITISH VIRGIN ISLANDS _1345. => congestion; //CAYMAN ISLANDS _1441. => congestion; //BERMUDA _1473. => congestion; //GRENADA _1649. => congestion; //TURKS & CAICOS ISLANDS _1664. => congestion; //MONTSERRAT _1758. => congestion; //ST. LUCIA _1767. => congestion; //DOMINICA _1784. => congestion; //ST. VINCENT & GRENADINES _1809. => congestion; //DOMINICAN REPUBLIC _1829. => congestion; //DOMINICAN REPUBLIC _1868. => congestion; //TRINIDAD AND TOBAGO _1869. => congestion; //ST. KITTS AND NEVIS _1876. => congestion; //JAMAICA _1787. => congestion; //Puerto Rico 787, 939 $0.07 _1939. => congestion; //Puerto Rico 787, 939 $0.07 _1671. => congestion; //Guam 671 $0.08 _1340. => congestion; //U.S. Virgin Islands 340 $0.06 }; context fromfwdOUT-catchall { _X. => Congestion; h => Hangup ; //hangup event i => Hangup ; //invalid event t => Hangup ; //timeout event }; context ael-demo { s => { Wait(1); Answer(); TIMEOUT(digit)=5; TIMEOUT(response)=10; restart: Background(demo-congrats); instructions: for (x=0; ${x} < 3; x=${x} + 1) { Background(demo-instruct); WaitExten(); }; }; 2 => { Background(demo-moreinfo); goto s|instructions; }; 3 => { LANGUAGE()=fr; goto s|restart; }; 500 => { Playback(demo-abouttotry); Dial(IAX2/guest@misery.digium.com); Playback(demo-nogo); goto s|instructions; }; 600 => { Playback(demo-echotest); Echo(); Playback(demo-echodone); goto s|instructions; }; _1234 => &std-exten-ael(${EXTEN}, "IAX2"); # => { Playback(demo-thanks); Hangup(); }; t => jump #; i => Playback(invalid); }; asterisk-1.4.21.2/pbx/ael/ael-test/ref.ael-test80000644000175000017500000000211511021261300020363 0ustar maniacmaniac (If you find progress and other non-error messages irritating, you can use -q to suppress them) (You can use the -w option to dump extensions.conf format to extensions.conf.aeldump) LOG: lev:2 file:pbx_ael.c line:4131 func: pbx_load_module Starting AEL load process. LOG: lev:2 file:pbx_ael.c line:4138 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:4146 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:4149 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:4151 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:4154 func: pbx_load_module AEL load process: merged config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:4157 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. LOG: lev:4 file:ael2_parse line:543 func: main 1 contexts, 7 extensions, 19 priorities asterisk-1.4.21.2/pbx/ael/ael-test/ael-vtest21/0000755000175000017500000000000011041455737020154 5ustar maniacmaniacasterisk-1.4.21.2/pbx/ael/ael-test/ael-vtest21/extensions.ael0000644000175000017500000000032010660114702023017 0ustar maniacmaniacglobals { AXLHAFT=wow-to-the-tenth-power; JibberWorthy=zinger3; OFFICE_CODE=503; } context from-enum { _${OFFICE_CODE}XXXX => { Answer(); goto ${EXTEN:3}|1; } } asterisk-1.4.21.2/pbx/ael/ael-test/ael-ntest23/0000755000175000017500000000000011041455737020146 5ustar maniacmaniacasterisk-1.4.21.2/pbx/ael/ael-test/ael-ntest23/t3/0000755000175000017500000000000011041455737020474 5ustar maniacmaniacasterisk-1.4.21.2/pbx/ael/ael-test/ael-ntest23/t3/j.ael0000644000175000017500000000005310767661433021414 0ustar maniacmaniac context j { 567 => NoOp(hi there, j); } asterisk-1.4.21.2/pbx/ael/ael-test/ael-ntest23/t3/i.ael0000644000175000017500000000005110767661433021411 0ustar maniacmaniaccontext i { 134 => NoOp(hi there, i); } asterisk-1.4.21.2/pbx/ael/ael-test/ael-ntest23/t3/g.ael0000644000175000017500000000005110767661433021407 0ustar maniacmaniaccontext g { 134 => NoOp(hi there, g); } asterisk-1.4.21.2/pbx/ael/ael-test/ael-ntest23/t3/h.ael0000644000175000017500000000005210767661433021411 0ustar maniacmaniac context h { 456 => NoOp(hithere, h); } asterisk-1.4.21.2/pbx/ael/ael-test/ael-ntest23/t2/0000755000175000017500000000000011041455737020473 5ustar maniacmaniacasterisk-1.4.21.2/pbx/ael/ael-test/ael-ntest23/t2/d.ael0000644000175000017500000000005110767661433021403 0ustar maniacmaniaccontext d { 134 => NoOp(hi there, d); } asterisk-1.4.21.2/pbx/ael/ael-test/ael-ntest23/t2/e.ael0000644000175000017500000000005210767661433021405 0ustar maniacmaniac context e { 456 => NoOp(hithere, e); } asterisk-1.4.21.2/pbx/ael/ael-test/ael-ntest23/t2/f.ael0000644000175000017500000000012210767661433021404 0ustar maniacmaniac#include "qq.ael" context f { 567 => NoOp(hi there, f); } #include "t3/*.ael" asterisk-1.4.21.2/pbx/ael/ael-test/ael-ntest23/t1/0000755000175000017500000000000011041455737020472 5ustar maniacmaniacasterisk-1.4.21.2/pbx/ael/ael-test/ael-ntest23/t1/a.ael0000644000175000017500000000005110767661433021377 0ustar maniacmaniaccontext a { 134 => NoOp(hi there, a); } asterisk-1.4.21.2/pbx/ael/ael-test/ael-ntest23/t1/b.ael0000644000175000017500000000005210767661433021401 0ustar maniacmaniac context b { 456 => NoOp(hithere, b); } asterisk-1.4.21.2/pbx/ael/ael-test/ael-ntest23/t1/c.ael0000644000175000017500000000015610767661433021407 0ustar maniacmaniac context c() { 567 => NoOp(hi there, c); } #include "t2/*.ael" context w() { 890 => NoOp(hi there, w); } asterisk-1.4.21.2/pbx/ael/ael-test/ael-ntest23/qq.ael0000644000175000017500000000005510767661433021260 0ustar maniacmaniac context qq { 567 => NoOp(hi there, qq); } asterisk-1.4.21.2/pbx/ael/ael-test/ael-ntest23/extensions.ael0000644000175000017500000000013410767661433023034 0ustar maniacmaniac#include "t1/*.ael" context z () { 123 => NoOp(hi there, z); 124 => NoOp(hi there, z); } asterisk-1.4.21.2/pbx/ael/ael-test/ael-test3/0000755000175000017500000000000011041455740017700 5ustar maniacmaniacasterisk-1.4.21.2/pbx/ael/ael-test/ael-test3/telemarket_torture.ael20000755000175000017500000006660410475447433024417 0ustar maniacmaniac// // AN EXCERSIZE IN BAD DIALPLAN DESIGN // (What better testing ground than on telemarketers?) // // BAD DESIGN: long, boring introductions followed by long, drawn out menus of choices. // if they survive to the last option, how will they remember the choices? // // BAD DESIGN: Amateur Recording. Poor voice quality, too quiet. // Also, the announcer is definitely not vocally gifted. // Also, the long pauses and clicks between the intro // and menu choices might lead some to think that // the announcements are over, and hang up. Too bad! // WORSE DESIGN: Instead of using the Background application, the Playback // application is used. After taking so much time and trouble // to record this material, the caller must listen and enjoy // every syllable before they can make an option choice. None // of that interrupting with a choice. We want them to savour // every word! // GOOD/BAD, ER INSIDIOUS -- DANGLE A CARROT-- GIVE THE LISTENER A GOOD REASON TO // HANG ON AND VOLUNTARILY LISTEN TO THE TORTURE. // BUT, DON'T MAKE PROMISES YOU WON'T KEEP! context telemarket { s => { begin: Playback(telemarketer-intro); // ; Script: // Due to the extremely high volume of calls from everything from telemarketers // to Septic System Bacteria vendors, we are asking all such organizations // to remove this number from their call list, or as need be, to add this // number to their No-Call list, whichever is relevent. // [THE CARROT:] // We HAVE made some exceptions, and if you wish to see if your organization // has been exempted, please listen to and follow the following prompts. // // Otherwise, please Cease calling this number! // Playback(telemarketer-choices); // if you represent a charitable organization, please dial 1, // if you represent a political organization, please dial 2. // if you represent a polling company, please dial 3, // if you represent a market research organization, please dial 4. // if you represent a magazine or newsletter, please dial 5. // if you represent a commercial organization, please dial 6. } 1 => goto telemarket-charity|s|begin; 2 => goto telemarket-political|s|begin; 3 => goto telemarket-pollster|s|begin; 4 => goto telemarket-research|s|begin; 5 => goto telemarket-magazine|s|begin; 6 => goto telemarket-commercial|s|begin; 7 => goto telemarket-other|s|begin; t => goto telemarket|s|begin; i => goto telemarket|s|begin; o => goto telemarket|s|begin; } context telemarket-charity { s => { begin: Playback(telemark-charity-intro); // We have contributed generously to many worthy causes in the past, and will // continue to do so in the future. But we suspect that such organizatons // have sold our name and phone number to each other until we are now hounded // day and night by literally hundreds of such organizations. // Enough is Enough! // // If we have contributed to your cause in the past, we may, perhaps, be disposed to // do so in the future, at our option, // we give no pledges nor make any commitments here. // Send us material via the post if you feel this necessary // but do not even consider email. Any email or further phone calls from your organization // in the future, will be considered an act of aggression, and we will // blacklist your organization for the rest of our natural lives. // // To see if your organization is exempt from these prohibitions, please // comply with the following options. Playback(telemark-charity-choices); // If your organization is disease or genetic defect related, dial 1, // If your organization is handicap related, dial 2. // If your organization is a police or fireman or other similar support entity, please dial 3. // If your organization is a grade school to high school related // fund raiser or other type of activity, please dial 4. // If your organization is a college or univerity or alumnis organization, please dial 5. // If your organization is animal rights or ecology related organization, please dial 6. // If your organization is a political action or candidate support related, please dial 7. // If your organization is a substance abuse related organization or cause, please dial 8. // And any other charity or tax exempt organization should dial 9. } 1 => goto telemarket-char-disease|s|begin; 2 => goto telemarket-char-handicap|s|begin; 3 => goto telemarket-char-police|s|begin; 4 => goto telemarket-char-school|s|begin; 5 => goto telemarket-char-college|s|begin; 6 => goto telemarket-char-animal|s|begin; 7 => goto telemarket-char-candidate|s|begin; 8 => goto telemarket-char-abuse|s|begin; 9 => goto telemarket-char-other|s|begin; // BAD DESIGN: referring all timeouts,invalid choices, etc, back to the root of the menu tree will frustrate users no end! // WORSE DESIGN: How about having the user have to push a button to repeat the current menu? When a time out could just // automatically do it for the user? t => goto telemarket|s|begin; i => goto telemarket|s|begin; o => goto telemarket|s|begin; } context telemarket-char-disease { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-char-handicap { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-char-police { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-char-school { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-char-college { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-char-animal { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-char-candidate { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-char-abuse { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-char-other { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-sorry { s => { begin: Playback(telemarket-sorry); // Sorry -- your organization is not exempt. Please stop calling us. // Thank you. goodbye. Hangup(); } } // BAD DESIGN: Hanging up on your audience, no matter what the outcome, is not a nice thing to do! context telemarket-exception { s => { begin: Playback(telemarket-success); // Congratulations. Your organization IS exempt. Please call us back, // but this time, just act like a normal caller. Thank you. Goodbye. Hangup(); } } // BAD DESIGN: Making long cascading menu choices is a nasty thing to do to callers! // BAD DESIGN: Putting the most frequently encountered items at the end of a list is also a nasty thing to do! // GOOD DESIGN: All rejection notices use a single context. All Acceptance also. To change a rejection to an // acceptance, just change the reference from telemarket-sorry to telemarket-exception context telemarket-political { s => { begin: Playback(telemark-polit-intro); // To see if your organization is exempt from our prohibitions, // please follow the following prompts. // please note that they are not in alphabetical order, and you will have to // give them your full attention. Playback(telemark-polit-choices); // if You represent the America First Party, dial 1. // if You represent the American Party, dial 2. // if You represent the American Heritage Party, dial 3. // if You represent the American Independent Party, dial 4. // if You represent the American Nazi Party, dial 5. // if You represent the Pot Party, dial 6. // if You represent the American Reform Party, dial 7. // if You represent the Christian Falenqist Party of America, dial 8. // all others, please dial 9. } 1 => goto telemarket-poli-Am1st|s|begin; 2 => goto telemarket-poli-American|s|begin; 3 => goto telemarket-poli-AmHer|s|begin; 4 => goto telemarket-poli-AmInd|s|begin; 5 => goto telemarket-poli-AmNaz|s|begin; 6 => goto telemarket-poli-Pot|s|begin; 7 => goto telemarket-poli-AmRef|s|begin; 8 => goto telemarket-poli-CFP|s|begin; 9 => goto telemarket-political2|s|begin; t => goto telemarket|s|begin; i => goto telemarket|s|begin; o => goto telemarket|s|begin; } context telemarket-political2 { s => { begin: Playback(telemark-politx-intro); // Thank you for your patience, and I congratulate you for your persistence. // Just a few more options! // Playback(telemark-polit2-choices); // if You represent the Communist Party USA, dial 1. // if You represent the Constitution Party, dial 2. // if You represent the Family Values Party, dial 3. // if You represent the Freedom Socialist Party, dial 4. // if You represent the Grass Roots Party, dial 5. // if You represent the Green Party, dial 6. // if You represent the Greens Party, dial 7. // if You represent the Independence Party, dial 8. // all others, goto 9. } 1 => goto telemarket-poli-Communist|s|begin; 2 => goto telemarket-poli-Constit|s|begin; 3 => goto telemarket-poli-FamVal|s|begin; 4 => goto telemarket-poli-FreedSoc|s|begin; 5 => goto telemarket-poli-Grassroot|s|begin; 6 => goto telemarket-poli-Green|s|begin; 7 => goto telemarket-poli-Greens|s|begin; 8 => goto telemarket-poli-Independence|s|begin; 9 => goto telemarket-political3|s|begin; t => goto telemarket|s|begin; i => goto telemarket|s|begin; o => goto telemarket|s|begin; } context telemarket-political3 { s => { begin: Playback(telemark-politx-intro); Playback(telemark-polit3-choices); // if You represent the Independant American Party, dial 1. // if You represent the Labor Party, dial 2. // if You represent the Libertarian Party, dial 3. // if You represent the Light Party, dial 4. // if You represent the Natural Law Party, dial 5. // if You represent the New Party, dial 6. // if You represent the New Union Party, dial 7. // if You represent the Peace and Freedom Party, dial 8. // all others, hang on, dial 9. } 1 => goto telemarket-poli-IndAm|s|begin; 2 => goto telemarket-poli-Labor|s|begin; 3 => goto telemarket-poli-Liber|s|begin; 4 => goto telemarket-poli-Light|s|begin; 5 => goto telemarket-poli-NatLaw|s|begin; 6 => goto telemarket-poli-New|s|begin; 7 => goto telemarket-poli-NewUn|s|begin; 8 => goto telemarket-poli-PeaceFree|s|begin; 9 => goto telemarket-political4|s|begin; t => goto telemarket|s|begin; i => goto telemarket|s|begin; o => goto telemarket|s|begin; } context telemarket-political4 { s => { begin: Playback(telemark-politx-intro); Playback(telemark-polit4-choices); // if You represent the Prohibition Party, dial 1. // if You represent the Reform Party, dial 2. // if You represent the Revolution , dial 3. // if You represent the Socialist Party USA, dial 4. // if You represent the Socialist Action Party, dial 5. // if You represent the Socialist Equality Party, dial 6. // if You represent the Socialist Labor Party, dial 7. // if You represent the Socialist Workers Party, dial 8. // all others, hang on, and dial 9. } 1 => goto telemarket-poli-Prohib|s|begin; 2 => goto telemarket-poli-Ref|s|begin; 3 => goto telemarket-poli-Revol|s|begin; 4 => goto telemarket-poli-SocPart|s|begin; 5 => goto telemarket-poli-SocAct|s|begin; 6 => goto telemarket-poli-SocEq|s|begin; 7 => goto telemarket-poli-SocLab|s|begin; 8 => goto telemarket-poli-SocWork|s|begin; 9 => goto telemarket-political5|s|begin; t => goto telemarket|s|begin; i => goto telemarket|s|begin; o => goto telemarket|s|begin; } context telemarket-political5 { s => { begin: Playback(telemark-politx-intro); Playback(telemark-polit5-choices); // if You represent the Southern Party, dial 1. // if You represent the Southern Independence Party, dial 2. // if You represent the US Pacifist Party, dial 3. // if You represent the We the People Party, dial 4. // if You represent the Workers World Party, dial 5. // if You represent the Democratic Party, dial 6. // if You represent the Republican Party, dial 7. // all others, may dial 8. } 1 => goto telemarket-poli-South|s|begin; 2 => goto telemarket-poli-SoInd|s|begin; 3 => goto telemarket-poli-USPac|s|begin; 4 => goto telemarket-poli-WTP|s|begin; 5 => goto telemarket-poli-WWP|s|begin; 6 => goto telemarket-poli-Democrat|s|begin; 7 => goto telemarket-poli-Repub|s|begin; 8 => goto telemarket-poli-other|s|begin; t => goto telemarket|s|begin; i => goto telemarket|s|begin; o => goto telemarket|s|begin; } context telemarket-poli-other { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-Repub { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-Democrat { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-WWP { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-WTP { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-USPac { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-SoInd { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-South { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-SocWork { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-SocLab { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-SocEq { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-SocAct { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-SocPart { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-Revol { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-Ref { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-Prohib { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-PeaceFree { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-NewUn { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-New { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-NatLaw { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-Light { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-Liber { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-Labor { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-IndAm { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-Independence { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-Greens { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-Green { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-Grassroot { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-FreedSoc { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-FamVal { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-Constit { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-Communist { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-CFP { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-AmRef { s => { begin: goto telemarket-sorry|s|begin; } } // BAD DESIGN: Putting in infinite loops in the menus, whether by design or mistake is not nice! context telemarket-poli-Pot { s => { begin: goto telemarket-political|s|begin; // will the Pot Party Guys even notice an infinite loop? } } context telemarket-poli-AmNaz { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-AmInd { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-AmHer { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-American { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-poli-Am1st { s => { begin: goto telemarket-sorry|s|begin; } } context telemarket-pollster { s => { begin: Playback(telemark-poll-intro); // I'm sorry-- We are just not available for doing any polling at the moment. So, // please remove us from your list. goto telemarket-sorry|s|begin; } t => goto telemarket|s|begin; i => goto telemarket|s|begin; o => goto telemarket|s|begin; } context telemarket-research { s => { begin: Playback(telemark-research-intro); // I'd like to say I'd love to help you with your market survey, but that would be a complete // and total lie. I am not interested in helping you with Market Surveys. // // Please remove me from your call list. It just doesn't pay enough. But Thank you. goto telemarket-sorry|s|begin; } t => goto telemarket|s|begin; i => goto telemarket|s|begin; o => goto telemarket|s|begin; } context telemarket-magazine { s => { begin: Playback(telemark-mag-choices); // If you are calling to see if I would like a NEW free subscription // to your magazine or newsletter, please dial 1. // If you are calling to see if I want to Renew an existing subscription, please dial 2. // If you are representing some publisher, and want my opinion about something, or are doing // some kind of survey, please dial 3. // If you are calling to verify that some previous caller actually called me, and the // verification information is correct, please dial 4. // and if your call purpose doesn't match any of the above, please dial 5. } 1 => goto telemark-mag-new|s|begin; 2 => goto telemark-mag-renew|s|begin; 3 => goto telemark-mag-survey|s|begin; 4 => goto telemark-mag-verify|s|begin; 5 => goto telemark-mag-other|s|begin; t => goto telemarket|s|begin; i => goto telemarket|s|begin; o => goto telemarket|s|begin; } context telemark-mag-new { s => { begin: Playback(telemark-mag-new); // I'm sorry, I'm maxed out, and the answer is NO. // If you really think I'd LOVE to add your publication to the pile I already get, // Send something via the post. Don't call me. // Thank you. bye. Hangup(); } t => goto telemarket|s|begin; i => goto telemarket|s|begin; o => goto telemarket|s|begin; } context telemark-mag-renew { s => { begin: Playback(telemark-mag-renew); // So, you want to see if I want to Renew, do you? The answer is most likely "YES". // // But, I will not answer a long list of questions over the phone. Send such // categorization info via the post, and stop bothering me over the phone, // if this is what you want. // Do you need verification information? Normally I opt out of such nonsense, if possible. // If not, use whatever of the following you can: // My birth month is October. // My birthplace is Kigali, in Rwanda, in Afica. // My eye color is orange. // All of these are wonderfully false, but I use them regularly for such purposes. Thank you. Hangup(); } t => goto telemarket|s|begin; i => goto telemarket|s|begin; o => goto telemarket|s|begin; } context telemark-mag-survey { s => { begin: Playback(telemark-mag-survey); // Sorry, I don't have time to answer survey or opinion questions. Find someone // else to help build your marketing database, I guess. Good Luck. Hangup(); } t => goto telemarket|s|begin; i => goto telemarket|s|begin; o => goto telemarket|s|begin; } context telemark-mag-verify { s => { begin: Playback(telemark-mag-verify); // If you are calling to verify that your own agents aren't ripping you off, // sorry, I can't help you. I opt out whenever I can, mainly because I'm not // paid enough for this kind of thing. I always lie, and I can't remember // what I might have said. Sorry. Goodbye. Hangup(); } t => goto telemarket|s|begin; i => goto telemarket|s|begin; o => goto telemarket|s|begin; } context telemark-mag-other { s => { begin: goto telemarket-sorry|s|begin; } } // BAD DESIGN: Is it entrapment, when you lure telemarketers to reveal their contact information, // Just so you can report them to the FTC/FCC? If it is, isn't it unethical for them // to hide their CID (via Anonymous, usually), to hide their identities from the public? // BTW -- What telemarketer would be stupid enough to fall for this? I'll bet not a single one! // For that matter, what telemarketer will be stupid enough to even enter any of this? I'll bet not a single one! // (but it was fun messing around). context telemarket-commercial { s => { begin: Playback(telemark-comm-intro); // Script: Please leave your name, organization, and phone number, plus // a short description of the purpose of your call, at the prompt. // We will do our best to respond to your call! And, in the mean time, // do not forget to add us to your no-call list! Voicemail(u82); goto telemarket-sorry|s|begin; } t => goto telemarket|s|begin; i => goto telemarket|s|begin; o => goto telemarket|s|begin; } context telemarket-other { s => { begin: Playback(telemark-other-intro); // Please review the previous menu options, and see if you really don't // fit in one of the previous categories. // If you do not, go ahead, and call me again, and let me know what category // I should have included in the above list. I appreciate this. Thank you much! Hangup(); } t => goto telemarket|s|begin; i => goto telemarket|s|begin; o => goto telemarket|s|begin; } asterisk-1.4.21.2/pbx/ael/ael-test/ael-test3/include2.ael20000644000175000017500000000014210423206707022147 0ustar maniacmaniac NoOp(This was included from include2.ael2); #include "include3.ael2" #include "include4.ael2" asterisk-1.4.21.2/pbx/ael/ael-test/ael-test3/include1.ael20000644000175000017500000000011610423206707022147 0ustar maniacmaniac NoOp(Hello, this is included from include1.ael2); #include "include2.ael2" asterisk-1.4.21.2/pbx/ael/ael-test/ael-test3/include4.ael20000644000175000017500000000012710423206707022154 0ustar maniacmaniac NoOp(This is include4.ael2! Isn't it cool!?!?!?!); NoOp(4 doesn't include anything); asterisk-1.4.21.2/pbx/ael/ael-test/ael-test3/include3.ael20000644000175000017500000000007110423206707022151 0ustar maniacmaniac NoOp(This is include3.ael2!); #include "include5.ael2" asterisk-1.4.21.2/pbx/ael/ael-test/ael-test3/include5.ael20000644000175000017500000000007010423206707022152 0ustar maniacmaniac NoOp(Include5.ael2 doesn't include anything, either!); asterisk-1.4.21.2/pbx/ael/ael-test/ael-test3/extensions.ael0000755000175000017500000022241410475447433022604 0ustar maniacmaniacglobals { static=yes; writeprotect=yes; CONSOLE=Console/dsp; // Console interface for demo IAXINFO=murf:tlhfckoct; // IAXtel username/password FWDNUMBER=544788 ; // your calling number FWDCIDNAME="Joe-Worker"; // your caller id FWDPASSWORD=zingledoodle ; // your password FWDRINGS=Zap/6 ; // the phone to ring FWDVMBOX=1 ; // the VM box for this user } macro std-exten( ext , dev ) { Dial(${dev}/${ext},20); switch(${DIALSTATUS}) { case BUSY: Voicemail(b${ext}); break; case NOANSWER: Voicemail(u${ext}); break; case ANSWER: break; default: Voicemail(u${ext}); } catch a { VoiceMailMain(${ext}); } } macro std-priv-exten_1( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_2( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_3( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_4( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_5( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_6( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_7( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_8( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_9( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_10( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_11( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_12( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_13( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_14( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_15( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_16( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_17( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_18( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_19( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_20( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_21( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_22( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_23( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_24( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_25( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_26( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_27( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_28( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_29( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_30( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_31( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_32( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_33( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_34( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_35( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_36( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_37( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_38( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_39( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_40( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_41( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_42( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_43( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_44( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_45( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_46( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_47( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_48( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_49( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_50( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_51( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_52( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_53( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_54( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_55( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_56( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_57( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_58( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_59( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_60( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_61( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_62( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_63( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_64( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_65( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_66( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_67( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_68( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_69( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_70( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_71( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_72( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten_73( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro std-priv-exten( dev, ext , timeout, opts, torcont, dontcont ) { Dial(${dev},${timeout},${opts}); NoOp(${DIALSTATUS} was chosen); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: Voicemail(b${ext}); break; case ANSWER: break; case NOANSWER: Voicemail(u${ext}); break; default: Voicemail(u${ext}); } } macro fillcidname() { if( "${CALLERID(number)}" = "" ) // nothing to work with, quit!!! return; Set(cidn=${DB(cidname/${CALLERID(num)})}); if( "${CALLERID(name)}" != "" ) { if( ("${cidn}" = "Privacy Manager" & "${CALLERID(name)}" != "Privacy Manager") | "${cidn}" = "" ) // if the entry isn't in the database, // or if an entry exists, and it's "Privacy Manager", empty, (or add other useless possibilities). { Set(DB(cidname/${CALLERID(number)})=${CALLERID(name)}); // then set or override what's in the DB } } // Now, we fill in the callerid info from the incoming entry, if it's stuff worth using // Ignore fundamentally semi-anonymous information from local cell phones // if the db has an entry for this number, and it's not a canned string from a cell phone company if( ( "${cidn}" != "" ) & ( "${CALLERID(name)}" = "" | "${CALLERID(name)}" = "CODY,WY " | "${CALLERID(name)}" = "POWELL,WY " | "${CALLERID(name)}" = "WIRELESS CALLER" | "${CALLERID(name)}" = "SUBSCRIBER,WIRE" | "${CALLERID(name)}" = "CELLULAR ONE" | "${CALLERID(name)}" = "Cellular One Customer" | "${CALLERID(name)}" = "CELLULAR ONE " | "${CALLERID(name)}" = "Privacy Manager" | "${CALLERID(name)}" = "RIVERTON,WY " | "${CALLERID(name)}" = "BASIN,WY " | "${CALLERID(name)}" = "BILLINGS,MT " | "${CALLERID(name)}" = "PROVO,UT " | "${CALLERID(name)}" = "TOLL FREE " ) ) // put stuff in the above, that the phone company tends to put in your callerid, // that you would rather override with DB info // there's no way to guess them all, but you can get the most popular ones... // why cell phones can't do CID like everybody else, ....? { Set(CALLERID(name)=${cidn}); // Override what the phone company provides with what's in the DB for this number. } } macro ciddial(dialnum, lookup, waittime, dialopts, ddev) { Set(cidnu=${CALLERID(num)}); Set(cidn=${DB(cidname/${lookup})}); Set(CALLERID(name)=${cidn}); Dial(${ddev}/${dialnum}|${waittime}|${dialopts}); if( "${DIALSTATUS}" = "CHANUNAVAIL" ) { BackGround(try_voip); CALLERID(num)=7075679201; Dial(SIP/1${lookup}@tctwest,${waittime},${dialopts}); if( "${DIALSTATUS}" = "CHANUNAVAIL" ) { BackGround(try_cell); CALLERID(num)=${cidnu}; // put the original number back Dial(Zap/2/${lookup},${waittime},${dialopts}); } } } macro ciddial3(dialnum, lookup, waittime, dialopts, ddev) { Set(cidnu=${CALLERID(num)}); Set(cidn=${DB(cidname/${lookup})}); Set(CALLERID(name)=${cidn}); Dial(${ddev}/${dialnum}|${waittime}|${dialopts}); if( "${DIALSTATUS}" = "CHANUNAVAIL" ) { BackGround(try_cell); Dial(Zap/2/${lookup},${waittime},${dialopts}); } } macro ciddial2(dialnum, lookup, waittime, dialopts, ddev) // give priority to tctwest, then the ZAP in emergencies { Set(cidn=${DB(cidname/${lookup})}); Set(cidnu=${CALLERID(num)}); Set(CALLERID(name)=${cidn}); Set(CALLERID(num)=7075679201); Dial(SIP/1${lookup}@tctwest,${waittime},${dialopts}); if( "${DIALSTATUS}" = "CHANUNAVAIL" ) { Set(CALLERID(num)=${cidnu}); // put the original number back BackGround(try_zap); Dial(${ddev}/${dialnum},${waittime}|${dialopts}); if( "${DIALSTATUS}" = "CHANUNAVAIL" ) { BackGround(try_cell); Dial(Zap/2/${lookup},${waittime},${dialopts}); } } } macro callerid-liar() { TrySystem(/usr/bin/play /var/lib/asterisk/sounds/priv-callerintros/LIAR.gsm&); Background(priv-liar); // Script: OOOps! Sorry! I don't allow men with ski masks pulled over their // faces to get in the front door, and unidentified callers won't fair // any better. You entered *MY* phone number. That won't work. // If you are telemarketing, cross me off the list, and don't call again. // If you did this by mistake, forgive my defenses, and call again. // Alternate: (priv-liar2) // Script: You have chosen to try to deceive my system and withold your CallerID, // by entering my own phone number as YOUR CallerID. I find this // offensive because you are being dishonest. I will not do business nor // waste my time talking to anyone who is less than honest and forthcoming. // Take me off your call list and do not call me again. Hangup(); } macro callerid-bad() { mycid=${CALLERID(num)}:"1([0-9]+)"; Set(CALLERID(num)=${mycid}); Wait(0); } context privacyManagerFailed { s => { begin: Background(PrivManInstructions); // Script: OOps, that didn't go well. You need to enter *your* area code, and *your* 7 digit // phone number, for a total of 10 digits, or you'll be handed over to the monkeys. Let's // try this again, and hopefully you can get past our front-line defenses! PrivacyManager(); if( "${PRIVACYMGRSTATUS}" = "FAILED" ) { Background(tt-allbusy); Background(tt-somethingwrong); Background(tt-monkeysintro); Background(tt-monkeys); Background(tt-weasels); Hangup(); } else { goto homeline|s|postPriv; } } } // Some comments // Some more comments context homeline { s => { begin: Answer(); Set(repeatcount=0); Zapateller(nocallerid); PrivacyManager(); if( "${PRIVACYMGRSTATUS}" = "FAILED" ) { TrySystem(/usr/bin/play /var/lib/asterisk/sounds/privmanfailed.gsm); &std-priv-exten(Zap/3r1&Zap/5r1,2,25,mtw,telemarket,telemarket); Hangup(); return; // goto privacyManagerFailed|s|begin; } postPriv: &fillcidname(); Set(CONFCIDNA=${CALLERID(name)}); Set(CONFCIDNU=${CALLERID(num)}); AGI(callall); AGI(submit-announce.agi); if( "${CALLERID(num)}" : "1" ) { &callerid-bad(); } if( "${CALLERID(num)}" = "7077577685" & "${CALLERID(name)}" : "Privacy Manager" ) { &callerid-liar(); } TrySystem(/usr/local/bin/who-is-it ${CALLERID(num)} "${CALLERID(name)}"&); Set(lds=${DB(playlds/${CALLERID(num)})}); if( "${lds}" = "1" ) { SetMusicOnHold(mohlds); } direct=${DB(DirectCall/${CALLERID(num)})}; if( "${direct}" != "" & ${direct} != 0 ) { verbose(direct is XXX#${direct}XXXX); Playback(greetings/direct); // Welcome to the Murphy residence. This system will automatically try to connect you to... Playback(/var/spool/asterisk/voicemail/default/${direct}/greet); TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm); TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/${direct}/greet.wav&); switch(${direct}) { case 1: //Steve &std-priv-exten(Zap/6r3&Sip/murf,1,25,mpA(beep)tw,telemarket,telemarket); goto s|loopback; case 2: //Sonya &std-priv-exten(Zap/3r1&Zap/5r1,2,25,mtw,telemarket,telemarket); goto s|loopback; default: // all the kids Set(z=${direct}-2); goto homeline-kids|${z}|1; } } loopback: ifTime(*|*|20-25|dec) { Playback(greetings/christmas); } else ifTime(*|*|31|dec) { Playback(greetings/newyear); } else ifTime(*|*|1|jan) { Playback(greetings/newyear); } else ifTime(*|*|14|feb) { Playback(greetings/valentines); } else ifTime(*|*|17|mar) { Playback(greetings/stPat); } else ifTime(*|*|31|oct) { Playback(greetings/halloween); } else ifTime(*|mon|15-21|jan) { Playback(greetings/mlkDay); } else ifTime(*|thu|22-28|nov) { Playback(greetings/thanksgiving); } else ifTime(*|mon|25-31|may) { Playback(greetings/memorial); } else ifTime(*|mon|1-7|sep) { Playback(greetings/labor); } else ifTime(*|mon|15-21|feb) { Playback(greetings/president); } else ifTime(*|sun|8-14|may) { Playback(greetings/mothers); } else ifTime(*|sun|15-21|jun) { Playback(greetings/fathers); } else { Playback(greetings/hello); // None of the above? Just a plain hello will do } Background(murphy-homeline-intro1); // Script: Hello-- Welcome to the Murphy's! If you already know what // option you want, you don't have to wait for this entire spiel-- just // have at it. // If you are calling because this number is on a list of some sort, dial 6. // If you want Sonya, dial 1. // If you want one of the kids, dial 2. // If you want Steve, dial 3. // to play with your introduction, dial 5. // If we don't seem to be giving you the time of day, try 7. // Have a good day! } 1 => { // Sonya TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm); TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/2/greet.wav&); &std-priv-exten(Zap/3r1&Zap/5r1,2,25,mtw,telemarket,telemarket); goto s|loopback; } 2 => { // Kids goto homeline-kids|s|begin; } 21 => { Dial(IAX2/seaniax,20,T); } 3 => { // Steve &std-priv-exten(Zap/6r3&Sip/murf,1,25,mpA(beep)tw,telemarket,telemarket); goto s|loopback; } 4 => { // Voicemail VoicemailMain(); goto s|loopback; } 5 => { // play with intro goto home-introduction|s|begin; } 6 => { // Telemarketers goto telemarket|s|begin; } 7 => { // time of day, riddle agi(tts-riddle.agi); Background(gsm/what-time-it-is2); SayUnixTime(); goto s|loopback; } 792 => { // Page All goto pageall|s|begin; } 793 => { // check the tone recognition Read(zz,,0,,1,0); SayDigits(${zz}); } t => { Set(repeatcount=${repeatcount} + 1); if( ${repeatcount} < 3 ) { goto s|loopback; // just loopback isn't enough } Hangup(); } i => { Background(invalid); goto s|loopback; } o => { Congestion(); } fax => { Dial(Zap/4); } } // Some comments // Some more comments context pageall { s => { begin: AGI(callall); MeetMe(5555,dtqp); MeetMeAdmin(5555,K); Hangup(); } h => { begin: MeetMeAdmin(5555,K); Background(conf-muted); Hangup(); } } // Some comments // Some more comments context add-to-conference { start => { NoCDR(); MeetMe(5555,dmqp); } h => { Hangup(); } } context home-introduction { s => { begin: Background(intro-options); // Script: To hear your Introduction, dial 1. // to record a new introduction, dial 2. // to return to the main menu, dial 3. // to hear what this is all about, dial 4. } 1 => { Playback(priv-callerintros/${CALLERID(num)}); goto s|begin; } 2 => { goto home-introduction-record|s|begin; } 3 => { goto homeline|s|loopback; } 4 => { Playback(intro-intro); // Script: // This may seem a little strange, but it really is a neat // thing, both for you and for us. I've taped a short introduction // for many of the folks who normally call us. Using the Caller ID // from each incoming call, the system plays the introduction // for that phone number over a speaker, just as the call comes in. // This helps the folks // here in the house more quickly determine who is calling. // and gets the right ones to gravitate to the phone. // You can listen to, and record a new intro for your phone number // using this menu. goto s|begin; } t => { goto s|begin; } i => { Background(invalid); goto s|begin; } o => { goto s|begin; } } context home-introduction-record { s => { begin: Background(intro-record-choices); // Script: // If you want some advice about recording your // introduction, dial 1. // otherwise, dial 2, and introduce yourself after // the beep. } 1 => { Playback(intro-record); // Your introduction should be short and sweet and crisp. // Your introduction will be limited to 10 seconds. // This is NOT meant to be a voice mail message, so // please, don't say anything about why you are calling. // After we are done making the recording, your introduction // will be saved for playback. // If you are the only person that would call from this number, // please state your name. Otherwise, state your business // or residence name instead. For instance, if you are // friend of the family, say, Olie McPherson, and both // you and your kids might call here a lot, you might // say: "This is the distinguished Olie McPherson Residence!" // If you are the only person calling, you might say this: // "This is the illustrious Kermit McFrog! Pick up the Phone, someone!!" // If you are calling from a business, you might pronounce a more sedate introduction,like, // "Fritz from McDonalds calling.", or perhaps the more original introduction: // "John, from the Park County Morgue. You stab 'em, we slab 'em!". // Just one caution: the kids will hear what you record every time // you call. So watch your language! // I will begin recording after the tone. // When you are done, hit the # key. Gather your thoughts and get // ready. Remember, the # key will end the recording, and play back // your intro. Good Luck, and Thank you!" goto 2|begin; } 2 => { begin: Background(intro-start); // OK, here we go! After the beep, please give your introduction. Background(beep); Record(priv-callerintros/${CALLERID(num)}:gsm,3); Background(priv-callerintros/${CALLERID(num)}); goto home-introduction|s|begin; } t => { goto s|begin; } i => { Background(invalid); goto s|begin; } o => { goto s|begin; } } context homeline-kids { s => { begin: Background(murphy-homeline-kids); // Which Kid? 1=Sean, 2:Eric, 3:Ryan, 4:Kyle, 5:Amber, 6:Alex, 7:Neal } 1 => { // SEAN TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm); TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/3/greet.wav&); // &std-priv-exten(Zap/3r2&Zap/5r2,3,35,mtw,telemarket,telemarket); &std-priv-exten(IAX2/seaniax&Zap/5r2,3,35,mtw,telemarket,telemarket); goto homeline|s|loopback; } 2 => { // ERIC TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm); TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/4/greet.wav&); Voicemail(u4); goto homeline|s|loopback; // SetMusicOnHold(erics); // TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm); // TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/4/greet.wav&); // &std-priv-exten(Zap/3r2&Zap/5r2,4,35,mtw,telemarket,telemarket); // goto homeline|s|loopback; } 3 => { // RYAN TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm); TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/5/greet.wav&); &std-priv-exten(Zap/3r2&Zap/5r2,5,35,mtw,telemarket,telemarket); goto homeline|s|loopback; } 4 => { // KYLE TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm); TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/6/greet.wav&); &std-priv-exten(Zap/3r2&Zap/5r2,6,35,mtw,telemarket,telemarket); goto homeline|s|loopback; } 5 => { TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm); TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/7/greet.wav&); &std-priv-exten(Zap/3r2&Zap/5r2,7,35,mtw,telemarket,telemarket); goto homeline|s|loopback; } 6 => { TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm); TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/8/greet.wav&); &std-priv-exten(Zap/3r2&Zap/5r2,8,35,mtw,telemarket,telemarket); goto homeline|s|loopback; } 7 => { TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm); TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/9/greet.wav&); &std-priv-exten(Zap/3r2&Zap/5r2,9,35,mtw,telemarket,telemarket); goto homeline|s|loopback; } t => { goto s|begin; } i => { Background(invalid); goto s|begin; } o => { goto s|begin; } } context voipworkline { s => { begin: Answer(); TrySystem(/usr/local/bin/who-is-it ${CALLERID(num)} "${CALLERID(name)}"&); goto workline|s|loopback; } 7075679201 => { Answer(); TrySystem(/usr/local/bin/who-is-it ${CALLERID(num)} "${CALLERID(name)}"&); goto workline|s|loopback; } } context workline { s => { begin: Answer(); Wait(1); Set(repeatcount=0); Zapateller(nocallerid); // PrivacyManager(); // if( "${PRIVACYMGRSTATUS}" = "FAILED" ) // { // goto privacyManagerFailed|s|begin; // } &fillcidname(); TrySystem(/usr/local/bin/who-is-it ${CALLERID(num)} "${CALLERID(name)}"&); loopback: Background(greetings/greeting); //script: Hello Background(murphy-office-intro1); //script: welcome to Steve Murphy's office. If you are dialing // this number because it was on a calling list of any sort, dial 6. // Otherwise, dial 1, and hopefully, you will reach Steve. } 1 => { TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm); TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/1/greet.wav&); &std-priv-exten(Zap/6&Sip/murf,1,30,mtw,telemarket,telemarket); goto s|loopback; } 4 => { VoicemailMain(); goto s|loopback; } 6 => { goto telemarket|s|begin; } 793 => { // check the tone recognition Read(zz,,0,,1,0); SayDigits(${zz}); } t => { repeatcount=${repeatcount} + 1; if( ${repeatcount} < 3 ) { goto s|loopback; // just loopback isn't enough } Hangup(); } i => { Background(invalid); goto s|loopback; } o => { Congestion(); } fax => { Answer(); Dial(Zap/4); } } context dialFWD { ignorepat => 8; ignorepat => 9; _83. => { Set(CALLERID(name)=${FWDCIDNAME}); Dial(IAX2/${FWDNUMBER}:${FWDPASSWORD}@iax2.fwdnet.net/${EXTEN:2},60,r); Congestion(); } _82NXX => { Set(CALLERID(name)=${FWDCIDNAME}); Dial(IAX2/${FWDNUMBER}:${FWDPASSWORD}@iax2.fwdnet.net/${EXTEN:2},60,r); Congestion(); } _92NXX => { Set(CALLERID(name)=${FWDCIDNAME}); Dial(IAX2/${FWDNUMBER}:${FWDPASSWORD}@iax2.fwdnet.net/${EXTEN:2},60,r); Congestion(); } } context dialiaxtel { ignorepat => 8; ignorepat => 9; _81700NXXXXXX => { Dial(IAX2/zorch:zilchnoodle@iaxtel.com/${EXTEN:1}@iaxtel); } _81800NXXXXXX => { Dial(IAX2/zorch:zilchnoodle@iaxtel.com/${EXTEN:1}@iaxtel); } _91700NXXXXXX => { Dial(IAX2/zorch:zilchnoodle@iaxtel.com/${EXTEN:1}@iaxtel); } _91800NXXXXXX => { Dial(IAX2/zorch:zilchnoodle@iaxtel.com/${EXTEN:1}@iaxtel); } } context dialgoiax { ignorepat => 9; _93. => { Set(CALLERID(name)="Joe Worker"); Dial(IAX2/878201007658:stickyfinger295@server1.goiax.com/${EXTEN:2},60,r); Congestion(); } } context homefirst { ignorepat => 9; _91NXXNXXXXXX => { &ciddial(${EXTEN:1},${EXTEN:2},30,TW,Zap/1); } _9754XXXX => { &ciddial(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1); } _9574XXXX => { &ciddial(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1); } _9202XXXX => { &ciddial(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1); } _9219XXXX => { &ciddial(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1); } _9254XXXX => { &ciddial(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1); } _9716XXXX => { &ciddial(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1); } _9NXXXXXX => { &ciddial(1707${EXTEN:1},707${EXTEN:1},30,TW,Zap/1); } _9011. => { &ciddial(${EXTEN:1},${EXTEN:1},30,TW,Zap/1); } _9911 => { Dial(Zap/1/911,30,T); } _9411 => { Dial(Zap/1/411,30,T); } } context workfirst { ignorepat => 9; _91NXXNXXXXXX => { &ciddial2(${EXTEN:1},${EXTEN:2},30,TW,Zap/1); } _9754XXXX => { &ciddial2(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1); } _9574XXXX => { &ciddial2(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1); } _9202XXXX => { &ciddial2(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1); } _9219XXXX => { &ciddial2(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1); } _9254XXXX => { &ciddial2(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1); } _9716XXXX => { &ciddial2(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1); } _9NXXXXXX => { &ciddial2(1707${EXTEN:1},707${EXTEN:1},30,TW,Zap/1); } _9911 => { Dial(Zap/1/911,30,T); } _9411 => { Dial(Zap/1/411,30,T); } } context force_cell { ignorepat => 8; _81NXXNXXXXXX => { &ciddial(${EXTEN:1}#,${EXTEN:2},30,TW,Zap/2); } _8754XXXX => { &ciddial(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/2); } _8574XXXX => { &ciddial(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/2); } _8202XXXX => { &ciddial(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/2); } _8219XXXX => { &ciddial(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/2); } _8254XXXX => { &ciddial(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/2); } _8716XXXX => { &ciddial(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/2); } _8NXXXXXX => { &ciddial(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/2); } _8911 => { Dial(Zap/1/911|30|T); } _8411 => { Dial(Zap/1/411|30|T); } } context force_home { ignorepat => 8; _81NXXNXXXXXX => { &ciddial3(${EXTEN:1}#,${EXTEN:2},30,TW,Zap/1); } _8754XXXX => { &ciddial3(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/1); } _8574XXXX => { &ciddial3(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/1); } _8202XXXX => { &ciddial3(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/1); } _8219XXXX => { &ciddial3(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/1); } _8254XXXX => { &ciddial3(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/1); } _8716XXXX => { &ciddial3(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/1); } _8NXXXXXX => { &ciddial3(1707${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/1); } _8911 => { Dial(Zap/1/911|30|T); } _8411 => { Dial(Zap/1/411|30|T); } } context homeext { ignorepat => 8; ignorepat => 9; includes { parkedcalls; homefirst; force_cell; } s => { loopback: Wait(0); } 1 => { &std-priv-exten(Zap/3&Zap/5,2,35,mtw,telemarket,telemarket); goto s|loopback; } 2 => { &std-priv-exten(Zap/6&Zap/5,1,35,mpA(beep3)Tt,telemarket,telemarket); goto s|loopback; } 4 => { VoicemailMain(); } 5 => { Record(recording:gsm); Background(recording); } 6 => { Background(recording); } 760 => { DateTime(); goto s|loopback; } 761 => { Record(announcement:gsm); TrySystem(/usr/bin/play /var/lib/asterisk/sounds/announcement.gsm&); goto s|loopback; } 762 => { agi(tts-riddle.agi); Background(gsm/what-time-it-is2); SayUnixTime(); goto s|loopback; } 763 => { Set(CALLERID(num)=); Dial(Zap/6r3,35,mptA(beep3)); //results: it should ALWAYS ask for an intro; the intro should not be left behind Hangup(); } 764 => { Set(CALLERID(num)=); Dial(Zap/6r3,35,mptnA(beep3)); //results: Don't save the intro; shouldn't anyway if no callerid Hangup(); } 765 => { Set(CALLERID(num)=); Dial(Zap/6r3,35,mptNA(beep3)); //results: Don't screen if there's CALLERID; it should screen the call. Hangup(); } 766 => { Dial(Zap/6r3,35,mptNA(beep3)); //results: Don't screen if there's CALLERID; it should screen the call. Hangup(); } 767 => { Dial(Zap/6r3,35,mptnA(beep3)); //results: Don't save the intro; the interesting case, because callerID should be present. Hangup(); } 769 => { Playtones(dial); Wait(2); Playtones(busy); Wait(2); Playtones(ring); Wait(2); Playtones(congestion); Wait(2); Playtones(callwaiting); Wait(2); Playtones(dialrecall); Wait(2); Playtones(record); Wait(2); Playtones(info); Wait(5); Hangup(); } 790 => { MeetMe(790,p); } 792 => { goto pageall|s|begin; } 795 => { AGI(wakeup.agi);Congestion(); } 544716 => { // Incoming call from FWD TrySystem(/usr/local/bin/who-is-it ${CALLERID(num)} "${CALLERID(name)}"&); goto s|loopback; } i => { Background(invalid); goto s|loopback; } o => { goto s|loopback; } t => { Congestion(); } } context fromvmhome { 1 => { Dial(Zap/6&Sip/murf|20|Tt); } 2 => { Dial(Zap/3&Zap/5|20|Tt); } _707202XXXX => { &ciddial(1${EXTEN:3},${EXTEN},30,TW,Zap/1); } _707219XXXX => { &ciddial(1${EXTEN:3},${EXTEN},30,TW,Zap/1); } _707254XXXX => { &ciddial(1${EXTEN:3},${EXTEN},30,TW,Zap/1); } _707716XXXX => { &ciddial(1${EXTEN:3},${EXTEN},30,TW,Zap/1); } _707754XXXX => { &ciddial(${EXTEN:3},${EXTEN},30,TW,Zap/1); } _707574XXXX => { &ciddial(${EXTEN:3},${EXTEN},30,TW,Zap/1); } _NXXNXXXXXX => { &ciddial(1${EXTEN},${EXTEN},30,TW,Zap/1); } _1NXXNXXXXXX => { // HAND DIALING &ciddial(${EXTEN},${EXTEN:1},30,TW,Zap/1); } _754XXXX => { &ciddial(${EXTEN},707${EXTEN},30,TW,Zap/1); } _574XXXX => { &ciddial(${EXTEN},707${EXTEN},30,TW,Zap/1); } _NXXXXXX => { &ciddial(1707${EXTEN},707${EXTEN},30,TW,Zap/1); } _911 => { &ciddial(911,911,30,TW,Zap/1); } _411 => { &ciddial(411,411,30,TW,Zap/1); } } context fromvmwork { 1 => { Dial(Zap/6&Sip/murf|20|Tt); } 2 => { Dial(Zap/3&Zap/5|20|Tt); } _707202XXXX => { &ciddial(1${EXTEN:3},${EXTEN},30,TW,Zap/1); } _707219XXXX => { &ciddial(1${EXTEN:3},${EXTEN},30,TW,Zap/1); } _707254XXXX => { &ciddial(1${EXTEN:3},${EXTEN},30,TW,Zap/1); } _707716XXXX => { &ciddial(1${EXTEN:3},${EXTEN},30,TW,Zap/1); } _707754XXXX => { &ciddial(${EXTEN:3},${EXTEN},30,TW,Zap/1); } _707574XXXX => { &ciddial(${EXTEN:3},${EXTEN},30,TW,Zap/1); } _NXXNXXXXXX => { &ciddial(1${EXTEN},${EXTEN},30,TW,Zap/1); } _1NXXNXXXXXX => { // HAND DIALING &ciddial(${EXTEN},${EXTEN:1},30,TW,Zap/1); } _754XXXX => { &ciddial(${EXTEN},707${EXTEN},30,TW,Zap/1); } _574XXXX => { &ciddial(${EXTEN},707${EXTEN},30,TW,Zap/1); } _NXXXXXX => { &ciddial(1707${EXTEN},707${EXTEN},30,TW,Zap/1); } 911 => { &ciddial(911,911,30,TW,Zap/1); } 411 => { &ciddial(411,411,30,TW,Zap/1); } } context fromSeanUniden { includes { parkedcalls; } 21 => { Dial(IAX2/seaniax,20,T); } _707202XXXX => { &ciddial(${EXTEN:3},${EXTEN},30,TW,Zap/1); } _707219XXXX => { &ciddial(${EXTEN:3},${EXTEN},30,TW,Zap/1); } _707254XXXX => { &ciddial(${EXTEN:3},${EXTEN},30,TW,Zap/1); } _707716XXXX => { &ciddial(${EXTEN:3},${EXTEN},30,TW,Zap/1); } _707754XXXX => { &ciddial(${EXTEN:3},${EXTEN},30,TW,Zap/1); } _707574XXXX => { &ciddial(${EXTEN:3},${EXTEN},30,TW,Zap/1); } _NXXNXXXXXX => { &ciddial(1${EXTEN},${EXTEN},30,TW,Zap/1); } _1NXXNXXXXXX => { &ciddial(${EXTEN},${EXTEN:1},30,TW,Zap/1); } _754XXXX => { &ciddial(${EXTEN},707${EXTEN},30,TW,Zap/1); } _574XXXX => { &ciddial(${EXTEN},707${EXTEN},30,TW,Zap/1); } _NXXXXXX => { &ciddial(1707${EXTEN},707${EXTEN},30,TW,Zap/1); } 911 => { &ciddial(911,911,30,TW,Zap/1); } 411 => { &ciddial(411,411,30,TW,Zap/1); } } context workext { ignorepat => 8; ignorepat => 9; includes { parkedcalls; workfirst; force_home; dialFWD; dialiaxtel; dialgoiax; } s => { loopback: Wait(0); } 1 => { Dial(Zap/3&Zap/5,20,tT); } 2 => { Dial(Zap/5&Zap/6,20,tT); } 21 => { Dial(IAX2/seaniax,20,T); } 22 => { Set(CALLERID(num)=1234567890); Set(CALLERID(name)=TestCaller); Dial(Zap/5,20,mP()A(beep)tw); NoOp(here is dialstatus: ${DIALSTATUS}...); goto s|loopback; } 4 => { VoicemailMain(); goto s|loopback; } 5 => { Record(recording:gsm); Background(recording); } 6 => { ZapBarge(); } 760 => { DateTime(); goto s|loopback; } 761 => { ZapBarge(); goto s|loopback; } 765 => { Playback(demo-echotest); Echo(); Playback(demo-echodone); goto s|loopback; } 766 => { Festival(The other thing to watch is neuro-electronics: the ability to interface technology with our neural system: My wife: Sigrid: has had a cochlear implant since 1996. This once profoundly deaf person now uses the phone: recognizes accents: and listens to movies and recorded books.); goto s|loopback; } 767 => { agi(tts-riddle.agi); Background(gsm/what-time-it-is2); SayUnixTime(); goto s|loopback; } 768 => { agi(tts-computer.agi); } 771 => { eagi(eagi-test); agi(my-agi-test); } 772 => { agi(wakeup.agi); } 775 => { if( ${EXTEN}=${EXTEN} ) { BackGround(digits/1); } else { BackGround(digits/0); } if( ${EXTEN}=${LANGUAGE} ) { BackGround(digits/1); } else { BackGround(digits/0); } BackGround(digits/2); } 776 => { Set(TEST=00359889811777); if( ${TEST}= 00359889811777 ) { BackGround(digits/1); } else { BackGround(digits/0); } if( ${TEST}= 00359889811888 ) { BackGround(digits/1); } else { BackGround(digits/0); } Hangup(); } 790 => { MeetMe(790,p); } 792 => { goto pageall|s|begin; } 793 => { #include "include1.ael2" } 795 => { AGI(wakeup.agi); Congestion(); } 797 => { Set(CONFCIDNA=${CALLERID(name)}); Set(CONFCIDNU=${CALLERID(num)}); AGI(callall); AGI(submit-announce.agi); Hangup(); } } context wakeup { 3 => { Dial(Zap/3|30); } 4 => { Dial(Zap/4|30); } 5 => { Dial(Zap/5|30); } 6 => { Dial(Zap/6|30); } 99 => { Dial(IAX2/murfiaxphone|30); } 97 => { Dial(IAX2/ryaniax|30); } 94 => { Dial(IAX2/seaniax|30); } } context announce-all { s => { begin: MeetMe(5555,dtqp); MeetMeAdmin(5555,K); Hangup(); } h => { MeetMeAdmin(5555,K); Hangup(); } } // now include the telemarketer torture scripts! #include "telemarket_torture.ael2" asterisk-1.4.21.2/pbx/ael/ael-test/ref.ael-test10000644000175000017500000000305611021261300020361 0ustar maniacmaniac (If you find progress and other non-error messages irritating, you can use -q to suppress them) (You can use the -w option to dump extensions.conf format to extensions.conf.aeldump) LOG: lev:2 file:pbx_ael.c line:4131 func: pbx_load_module Starting AEL load process. LOG: lev:2 file:pbx_ael.c line:4138 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:4146 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. LOG: lev:3 file:pbx_ael.c line:2235 func: check_switch_expr Warning: file ./extensions.ael, line 58-58: A default case was automatically added to the switch. LOG: lev:3 file:pbx_ael.c line:938 func: check_dow Warning: file ./extensions.ael, line 67-67: The day (m0n) must be one of 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', or 'sat'! LOG: lev:3 file:pbx_ael.c line:896 func: check_timerange Warning: file ./extensions.ael, line 78-78: The end time (25:00) is out of range! LOG: lev:2 file:pbx_ael.c line:4149 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:4151 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:4154 func: pbx_load_module AEL load process: merged config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:4157 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. LOG: lev:4 file:ael2_parse line:543 func: main 5 contexts, 16 extensions, 161 priorities asterisk-1.4.21.2/pbx/ael/ael-test/ael-ntest12/0000755000175000017500000000000011041455740020136 5ustar maniacmaniacasterisk-1.4.21.2/pbx/ael/ael-test/ael-ntest12/extensions.ael0000644000175000017500000000041310463722604023021 0ustar maniacmaniaccontext test1 { 771 => { for( i=0; ${i} <= 3; i = ${i} + 1 ) NoOp(i is '${i}'); } 772 => { for(i=0; ${i} <= 3;i= ${i} + 1 ) NoOp(i is '${i}'); } } asterisk-1.4.21.2/pbx/ael/ael-test/ael-test8/0000755000175000017500000000000011041455740017705 5ustar maniacmaniacasterisk-1.4.21.2/pbx/ael/ael-test/ael-test8/extensions.ael0000644000175000017500000000145410445343530022573 0ustar maniacmaniaccontext default { 706/3077610011 => { JabberStatus(asterisk|jmls@mike,StatusCode); switch(${StatusCode}) { case 1: Dial(SIP/706,12); switch(${DIALSTATUS}) { case BUSY: Voicemail(b706); break; default: Voicemail(u706); }; BackGround(hello); break; default: Voicemail(u706); }; Hangup(); }; } asterisk-1.4.21.2/pbx/ael/ael-test/ref.ael-test40000644000175000017500000000533210677554425020420 0ustar maniacmaniac (If you find progress and other non-error messages irritating, you can use -q to suppress them) (You can use the -w option to dump extensions.conf format to extensions.conf.aeldump) LOG: lev:2 file:pbx_ael.c line:4048 func: pbx_load_module Starting AEL load process. LOG: lev:2 file:pbx_ael.c line:4055 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. LOG: lev:2 file:ael.flex line:654 func: setup_filestack --Read in included file ./apptest.ael2, 3474 chars LOG: lev:3 file:ael.y line:529 func: ael_yyparse ==== File: ./apptest.ael2, Line 46, Cols: 8-11: Suggestion: Use the goto statement instead of the Goto() application call in AEL. LOG: lev:2 file:pbx_ael.c line:4063 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. LOG: lev:3 file:pbx_ael.c line:2392 func: check_pval_item Warning: file ./apptest.ael2, line 35-35: application call to EndWhile needs to be re-written using AEL if, while, goto, etc. keywords instead! LOG: lev:3 file:pbx_ael.c line:2392 func: check_pval_item Warning: file ./apptest.ael2, line 37-37: application call to ExecIf needs to be re-written using AEL if, while, goto, etc. keywords instead! LOG: lev:3 file:pbx_ael.c line:1287 func: check_goto Warning: file ./apptest.ael2, line 46-46: goto: no context cont could be found that matches the goto target! LOG: lev:3 file:pbx_ael.c line:2392 func: check_pval_item Warning: file ./apptest.ael2, line 47-47: application call to GotoIf needs to be re-written using AEL if, while, goto, etc. keywords instead! LOG: lev:3 file:pbx_ael.c line:2392 func: check_pval_item Warning: file ./apptest.ael2, line 48-48: application call to GotoIfTime needs to be re-written using AEL if, while, goto, etc. keywords instead! LOG: lev:3 file:pbx_ael.c line:2392 func: check_pval_item Warning: file ./apptest.ael2, line 85-85: application call to Random needs to be re-written using AEL if, while, goto, etc. keywords instead! LOG: lev:3 file:pbx_ael.c line:2392 func: check_pval_item Warning: file ./apptest.ael2, line 141-141: application call to While needs to be re-written using AEL if, while, goto, etc. keywords instead! LOG: lev:2 file:pbx_ael.c line:4066 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:4068 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:4071 func: pbx_load_module AEL load process: merged config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:4074 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. LOG: lev:4 file:ael2_parse line:527 func: main 1 contexts, 1 extensions, 142 priorities asterisk-1.4.21.2/pbx/ael/ael-test/ael-test2/0000755000175000017500000000000011041455740017677 5ustar maniacmaniacasterisk-1.4.21.2/pbx/ael/ael-test/ael-test2/apptest.ael20000644000175000017500000000662210423206707022132 0ustar maniacmaniac// this is a quick test to see how many of the apps we can spot j options in // include this in a macro or extension // at this moment, there are 18 apps that accept the j option. AddQueueMember(zork,iface,20,j); ADSIProg(sfile); AgentCallbackLogin(agent,s,30@cont); AgentLogin(agent,s); AgentMonitorOutgoing(dcn); AGI(whatever); AlarmReceiver(); Answer(2); AppendCDRUserField(value); Authenticate(pword,adjmr); BackGround(filename,snm,eng); BackgroundDetect(filename,20,2,10); Busy(10); ChangeMonitor(fnamebase); ChanIsAvail(Zap/5,sj); ChanSpy(prefix,bg()qrv); Congestion(5); ControlPlayback(filename,10,6,4,0,5,7,j); DateTime(unixtime,tz,fmt); DBdel(fam/key); DBdeltree(fam); DeadAGI(command); Dial(zap/1,45,A()CdD()fgG()hHjL()m()M()nNoprS()tTwW); Dictate(basedir); Directory(cont,dcont,f); DISA(68986869876,context); DumpChan(verblev); DUNDiLookup(90709780978,context,bj); EAGI(command); Echo(); EndWhile(); Exec(appname,args); ExecIf(expr,app,data); ExecIfTime(*,*,*,*,appname); ExternalIVR(command,arg1); Festival(text); Flash(); ForkCDR(v); GetCPEID(); Gosub(cont,exten,priority); GosubIf(cond?label); Goto(cont,exten,prior); GotoIf(cond?t:f); GotoIfTime(*,*,*,*?cont,ext,prior); Hangup(); HasNewVoicemail(vmbox,var,j); HasVoicemail(vmbox,var,j); IAX2Provision(template); ICES(xmlconfig); ImportVar(nevar@chann,var); Log(NOTICE,message); LookupBlacklist(j); LookupCIDName(); Macro(macro,arg1); MacroExit(); MacroIf(expr?etc); MailboxExists(mbox@cont,j); Math(v,2+2); MeetMe(5555,aAbcdDeimMpPqrstTovwxX); MeetMeAdmin(5555,e,user); MeetMeCount(5555,var); Milliwatt(); MixMonitor(filename,abv()V()W(),command); Monitor(file.fmt,base,mb); MP3Player(location); MusicOnHold(class); NBScat(); NoCDR(); NoOp(ignored); Page(Zap/1,dq); Park(exten); ParkAndAnnounce(template,5,238,retcont); ParkedCall(exten); PauseQueueMember(queue,zap,j); Pickup(ext@cont); Playback(file,j); PlayTones(arg); PrivacyManager(3,4,j); Progress(); Queue(queuename,dhHnrtTwW,http://www.where.what,over,5); Random(30,cont,ext,pri); Read(var,fname,10,skip,2,5); ReadFile(var=file,10); RealTime(fam,2,val,prefix); RealTimeUpdate(fam,2,val,2,newval); Record(file,2,10,anqst); RemoveQueueMember(queuename,iface,j); ResetCDR(wav); RetryDial(annound,4,2); Return(); Ringing(); RxFAX(fname,caller); SayAlpha(string); SayDigits(string); SayNumber(digits); SayPhonetic(string); SayUnixTime(unixtime,tz,fmt); SendDTMF(digits,10); SendImage(filename); SendText(text,j); SendURL(URL); Set(a=b); SetAMAFlags(); SetCallerID(clid,a); SetCallerPres(allowed_passed_screen); SetCDRUserField(value); SetGlobalVar(var=val); SetMusicOnHold(class); SetTransferCapability(SPEECH); SIPAddHeader(header); SIPDtmfMode(inband,info,rfc); SIPGetHeader(var@headername); SMS(name); SoftHangup(zap/1,a); StackPop(); StartMusicOnHold(class); StopMonitor(); StopMusicOnHold(); StopPlayTones(); System(command); TestClient(testid); TestServer(); Transfer(zap/1,j); TrySystem(command); TxFAX(filename,caller,debug); UnpauseQueueMember(queuename,iface,j); UserEvent(eventanme,body); Verbose(5,message); VMAuthenticate(mailbox@cont,s); VoiceMail(mailbox@cont,bg()suj); VoiceMailMain(mailbox@cont,pg()s); Wait(2); WaitExten(3,m()); WaitForRing(2); WaitForSilence(2,y); WaitMusicOnHold(2); While(expr); Zapateller(answer,5); ZapBarge(channel); ZapRAS(arg); ZapScan(group); ZapSendKeypadFacility(); asterisk-1.4.21.2/pbx/ael/ael-test/ael-test2/extensions.ael0000644000175000017500000000007310423206707022561 0ustar maniacmaniaccontext test1 { s => { #include "apptest.ael2"; } } asterisk-1.4.21.2/pbx/ael/ael-test/ael-test14/0000755000175000017500000000000011041455740017762 5ustar maniacmaniacasterisk-1.4.21.2/pbx/ael/ael-test/ael-test14/extensions.ael0000644000175000017500000000034110473660407022650 0ustar maniacmaniaccontext test1 { 10 => { // nothing but a comment! } 11 => { switch(${somevar}) { case somecase: // nothing but a comment! break; case somecase: // nothing but a comment! continue; } break; } } asterisk-1.4.21.2/pbx/ael/ael-test/ref.ael-vtest210000644000175000017500000000026610660114702020644 0ustar maniacmaniac[globals] AXLHAFT=wow-to-the-tenth-power JibberWorthy=zinger3 OFFICE_CODE=503 [from-enum] exten => _${OFFICE_CODE}XXXX,1,Answer() exten => _${OFFICE_CODE}XXXX,2,Goto(${EXTEN:3}|1) asterisk-1.4.21.2/pbx/ael/ael-test/ref.ael-test30000644000175000017500000000336411021261300020365 0ustar maniacmaniac (If you find progress and other non-error messages irritating, you can use -q to suppress them) (You can use the -w option to dump extensions.conf format to extensions.conf.aeldump) LOG: lev:2 file:pbx_ael.c line:4131 func: pbx_load_module Starting AEL load process. LOG: lev:2 file:pbx_ael.c line:4138 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. LOG: lev:2 file:ael.flex line:663 func: setup_filestack --Read in included file ./include1.ael2, 78 chars LOG: lev:2 file:ael.flex line:663 func: setup_filestack --Read in included file ./include2.ael2, 98 chars LOG: lev:2 file:ael.flex line:663 func: setup_filestack --Read in included file ./include3.ael2, 57 chars LOG: lev:2 file:ael.flex line:663 func: setup_filestack --Read in included file ./include5.ael2, 56 chars LOG: lev:2 file:ael.flex line:663 func: setup_filestack --Read in included file ./include4.ael2, 87 chars LOG: lev:2 file:ael.flex line:663 func: setup_filestack --Read in included file ./telemarket_torture.ael2, 28036 chars LOG: lev:2 file:pbx_ael.c line:4146 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:4149 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:4151 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:4154 func: pbx_load_module AEL load process: merged config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:4157 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. LOG: lev:4 file:ael2_parse line:543 func: main 172 contexts, 934 extensions, 2478 priorities asterisk-1.4.21.2/pbx/ael/ael-test/ref.ael-test70000644000175000017500000000330110616364660020406 0ustar maniacmaniac (If you find progress and other non-error messages irritating, you can use -q to suppress them) (You can use the -w option to dump extensions.conf format to extensions.conf.aeldump) LOG: lev:2 file:pbx_ael.c line:3915 func: pbx_load_module Starting AEL load process. LOG: lev:2 file:pbx_ael.c line:3922 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. LOG: lev:2 file:pbx_ael.c line:3930 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. LOG: lev:4 file:pbx_ael.c line:2339 func: check_pval_item Error: file ./extensions.ael, line 98-98: The macro call to checkanddial has 5 arguments, but the macro definition has 7 arguments LOG: lev:4 file:pbx_ael.c line:2339 func: check_pval_item Error: file ./extensions.ael, line 107-107: The macro call to checkanddial has 5 arguments, but the macro definition has 7 arguments LOG: lev:4 file:pbx_ael.c line:2339 func: check_pval_item Error: file ./extensions.ael, line 284-284: The macro call to checkanddial has 5 arguments, but the macro definition has 7 arguments LOG: lev:4 file:pbx_ael.c line:2339 func: check_pval_item Error: file ./extensions.ael, line 287-287: The macro call to checkanddial has 5 arguments, but the macro definition has 7 arguments LOG: lev:3 file:pbx_ael.c line:2320 func: check_pval_item Warning: file ./extensions.ael, line 452-452: macro call to non-existent std-exten-ael ! Hopefully it is present in extensions.conf! LOG: lev:4 file:pbx_ael.c line:3943 func: pbx_load_module Sorry, but 0 syntax errors and 4 semantic errors were detected. It doesn't make sense to compile. LOG: lev:4 file:ael2_parse line:512 func: main 0 contexts, 0 extensions, 0 priorities asterisk-1.4.21.2/pbx/ael/ael-test/ael-test19/0000755000175000017500000000000011041455740017767 5ustar maniacmaniacasterisk-1.4.21.2/pbx/ael/ael-test/ael-test19/extensions.ael0000644000175000017500000002200110616372261022647 0ustar maniacmaniaccontext dialextens { /* 101 thru 123, 149 thru 152 */ _10X => Dial(Zap/${EXTEN:2},30,Ttw); _1ZX => Dial(Zap/${EXTEN:1},30,Ttw); } /* Due to extenal wiring: dialing 125 will ring 101 dialing 126 will ring 102 and so on until dialing 147 will ring 123 We can dial out on zap 69 thru 72; and 25-47 */ context dialthrus { /* 369-372; 325-347 */ _3XX => Dial(Zap/${EXTEN:1},30,Ttw); } context t1incoming { includes { dialextens; parkedcalls; } s => { Answer(); Background(welcome-to-test-machine); } } context t1extension { includes { dialextens; dialthrus; } } context incoming { includes { dialextens; parkedcalls; } s => { Answer(); Background(welcome-to-test-machine); } } context incoming { s => { Answer(); } } macro std-priv-exten( dev, ext , timeout, opts, torcont, dontcont ) { // &increment_chosecount(); dial_again: Dial(${dev},${timeout},${opts}); switch(${DIALSTATUS}) { case TORTURE: goto ${torcont}|s|begin; break; case DONTCALL: goto ${dontcont}|s|begin; break; case BUSY: label_busy: Read(reply|work-exten-busy|1||2|15); if ("${reply}"=="") goto label_busy; // infinite loop if Read has probs!! switch(${reply}) { case 1: Set(time1=${EPOCH}); label_redial: WaitMusicOnHold(5); Dial(${dev},${timeout},${opts}); switch(${DIALSTATUS}) { case BUSY: if(${EPOCH}-${time1} >= 20) goto label_busy; goto label_redial; default: return;// goto work_line|s|loopback; } break; case 2: Voicemail(${ext}|b); break; case 3: return; // goto work_line|s|loopback; default: Background(invalid); goto label_busy; } break; case ANSWER: break; case NOANSWER: noanswer_label: Read(reply|work-exten-noanswer|1|skip|2|15); switch(${reply}) { case 1: switch(${ext}) { case 10: Background(no-cell); break; case 11: // &ciddial(2729495,3072729495,30,tw,${GRAMS_TELCO},${WORK_TELCO}); break; case 12: // &ciddial(2725560,3072725560,30,tw,${GRAMS_TELCO},${WORK_TELCO}); break; case 13: // &ciddial(2720197,3072720197,30,tw,${GRAMS_TELCO},${WORK_TELCO}); break; case 14: // &ciddial(2501174,3072501174,30,tw,${GRAMS_TELCO},${WORK_TELCO}); break; case 15: Background(no-cell); break; case 16: Background(no-cell); break; default: Background(invalid); break; } goto noanswer_label; break; case 2: Voicemail(${ext}|u); break; case 3: return; // goto work_line|s|loopback; default: Background(invalid); goto noanswer_label; } Voicemail(${ext}|u); break; default: Voicemail(${ext}|u); } } /* Putting these 3 funcs in extensions.conf! macro funcC(a,b) { Set(Key=); menu: Read(Key,main-menu,1,n,1,5); if("${Key}" = "2") goto y,lab1; catch y { lab1: &funcB(${a},${b}); } } macro funcB(a,b) { Set(Key=); menu: Read(Key,tt-monkeys,1,n,1,5); if("${Key}" = "2") goto z,lab2; catch z { lab2: &funcC(${a},${b}); } } macro funcA() { &funcB(1,2); } */ context extension { includes { dialextens; dialthrus; parkedcalls; } 5 => { Record(recording:wav); Background(recording); } 81 => { iterations=1000000; Set(time1=${EPOCH}); for(i=1; ${i}<${iterations}; i=${i}+1) { NoOp(Hello); } Set(time2=${EPOCH}); Verbose(The time diff is $[${time2} - ${time1} ] seconds); Verbose(Which means that the priorities/sec = $[4* ${iterations} / (${time2} - ${time1}) ]); SayNumber($[4 * ${iterations} / (${time2} - ${time1}) ]); } 82 => { &ndeep(100000); Verbose(Finished 100000 levels deep call!); } 83 => { switch (${EXTEN}) { pattern 8X: Verbose(do something to prepare it); pattern 9X: Verbose(handle both 1xx and 2xx calls); pattern [4-7]X: Verbose(and this too!); } Set(junky=${RAND(0|99999)}); Verbose(Here is a random number: ${junky}.); } 84 => { agi(agi://192.168.134.252/|hello|goodbye|whatever|whoknows,hell2,hello3); } 85 => { &std-priv-exten( Zap/50, 150 , 25, mtw, torcont, dontcont ); } 86 => { Verbose(The version is: ${VERSION()} ); Verbose(The versionnum is: ${VERSION(ASTERISK_VERSION_NUM)} ); Verbose(The user is: ${VERSION(BUILD_USER)} ); Verbose(The hostname is: ${VERSION(BUILD_HOSTNAME)} ); Verbose(The machine is: ${VERSION(BUILD_MACHINE)} ); Verbose(The OS is: ${VERSION(BUILD_OS)} ); Verbose(The date is: ${VERSION(BUILD_DATE)} ); Verbose(The kernel is: ${VERSION(BUILD_KERNEL)} ); Set(vinf=${VERSION()}); Set(vrand=${RAND()}); if( ${ISNULL(${vinf})} ) { if( ${ISNULL(${vrand})} ) Verbose(Version 1.2 or earlier); else Verbose(Version 1.4!!!); } else Verbose(${vinf} indicates version pre-1.6 or higher); } 871 => { NoOp( 1 1 1 1 1 1 1); NoOp( 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6); NoOp(012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678890123456789012345678901234567890); NoOp(${EXTEN:1:2} ${EXTEN} ${EXTEN:1} 1 1 1 1 1 1 1); &dialoutpstn(${TDIRECTCALL-PST}/0${EXTEN},${E${CALLERID(num)}-OPT},${TDIRECTCALL-CID},${TDIRECTCALL-MAX},RotaPadrao) ; } 872 => { Set(ChannelOnly=${CUT(CHANNEL||1)}); Verbose(ChannelOnly=${ChannelOnly}; neat huh?); Set(ChannelOnly=${CUT(CHANNEL,,1)}); Verbose(ChannelOnly=${ChannelOnly}; neat huh?); } 873 => { NOOP(this is a forkcdr test); Set(CALLERID(number)=1234567890); Set(CALLERID(name)=before fork); Forkcdr(v); Set(CALLERID(number)=0987654321); Set(CALLERID(name)=after fork); Answer(); Echo(); Hangup(); } 874 => { SayDigits(307-754-5675); SayPhoneNumber(307-754-5675); SayDigits(--); SayPhoneNumber(123-456-7890); SayDigits(++); SayPhoneNumber(307-754-4454); } 875 => { &funcA(); &funcD(); } 876 => { NoOp(Query resultid ${connid} SELECT var1\, var2 FROM did); NoOp($["Query resultid ${connid} SELECT var1\, var2 FROM did"]); NoOp($["Query resultid ${connid} SELECT var1, var2 FROM did"]); goto test5,s,1; } 88 => { SET(LIMIT_PLAYAUDIO_CALLER=yes); SET(LIMIT_PLAYAUDIO_CALLEE=no); SET(LIMIT_TIMEOUT_FILE=timeup); SET(LIMIT_CONNECT_FILE=limit60); SET(LIMIT_WARNING_FILE=almostup); Dial(Zap/51,20,L(60000:30000:8000)); } 89 => { goto callbackmenu|100|1; } } context income1 { s => { Answer(); Dial(Zap/50,20,m); } 150 => Dial(Zap/50,20,m); } context callbackmenu { _X. => { Answer(); Wait(1); Set(TIMEOUT(digit)=5); Set(TIMEOUT(response)=30); DISA(no-password,callbackdialout); } } context callbackdialout { _X. => { Dial(Zap/51,20,w); } } macro dialoutpstn(something1, something2, something3, something4, something5) { Verbose(${something1}--- ${something2}--- ${something3}--- ${something4}--- ${something5}); } macro ndeep(level) { if( ${level} == 0) { Verbose(2|Got to Level 0); return; } &ndeep($[${level}-1]); return; } asterisk-1.4.21.2/pbx/ael/ael_lex.c0000644000175000017500000026432510767661433016166 0ustar maniacmaniac#line 2 "ael_lex.c" #line 4 "ael_lex.c" #define YY_INT_ALIGNED short int /* A lexical scanner generated by flex */ #define FLEX_SCANNER #define YY_FLEX_MAJOR_VERSION 2 #define YY_FLEX_MINOR_VERSION 5 #define YY_FLEX_SUBMINOR_VERSION 33 #if YY_FLEX_SUBMINOR_VERSION > 0 #define FLEX_BETA #endif /* First, we deal with platform-specific or compiler-specific issues. */ #include "asterisk.h" /* begin standard C headers. */ #include #include #include #include /* end standard C headers. */ /* flex integer type definitions */ #ifndef FLEXINT_H #define FLEXINT_H /* C99 systems have . Non-C99 systems may or may not. */ #if !defined __STDC_VERSION__ || __STDC_VERSION__ >= 199901L /* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, * if you want the limit (max/min) macros for int types. */ #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS 1 #endif #include typedef int8_t flex_int8_t; typedef uint8_t flex_uint8_t; typedef int16_t flex_int16_t; typedef uint16_t flex_uint16_t; typedef int32_t flex_int32_t; typedef uint32_t flex_uint32_t; #else typedef signed char flex_int8_t; typedef short int flex_int16_t; typedef int flex_int32_t; typedef unsigned char flex_uint8_t; typedef unsigned short int flex_uint16_t; typedef unsigned int flex_uint32_t; #endif /* ! C99 */ /* Limits of integral types. */ #ifndef INT8_MIN #define INT8_MIN (-128) #endif #ifndef INT16_MIN #define INT16_MIN (-32767-1) #endif #ifndef INT32_MIN #define INT32_MIN (-2147483647-1) #endif #ifndef INT8_MAX #define INT8_MAX (127) #endif #ifndef INT16_MAX #define INT16_MAX (32767) #endif #ifndef INT32_MAX #define INT32_MAX (2147483647) #endif #ifndef UINT8_MAX #define UINT8_MAX (255U) #endif #ifndef UINT16_MAX #define UINT16_MAX (65535U) #endif #ifndef UINT32_MAX #define UINT32_MAX (4294967295U) #endif #endif /* ! FLEXINT_H */ #ifdef __cplusplus /* The "const" storage-class-modifier is valid. */ #define YY_USE_CONST #else /* ! __cplusplus */ #if __STDC__ #define YY_USE_CONST #endif /* __STDC__ */ #endif /* ! __cplusplus */ #ifdef YY_USE_CONST #define yyconst const #else #define yyconst #endif /* Returned upon end-of-file. */ #define YY_NULL 0 /* Promotes a possibly negative, possibly signed char to an unsigned * integer for use as an array index. If the signed char is negative, * we want to instead treat it as an 8-bit unsigned char, hence the * double cast. */ #define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) /* An opaque pointer. */ #ifndef YY_TYPEDEF_YY_SCANNER_T #define YY_TYPEDEF_YY_SCANNER_T typedef void* yyscan_t; #endif /* For convenience, these vars (plus the bison vars far below) are macros in the reentrant scanner. */ #define yyin yyg->yyin_r #define yyout yyg->yyout_r #define yyextra yyg->yyextra_r #define yyleng yyg->yyleng_r #define yytext yyg->yytext_r #define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) #define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) #define yy_flex_debug yyg->yy_flex_debug_r int ael_yylex_init (yyscan_t* scanner); /* Enter a start condition. This macro really ought to take a parameter, * but we do it the disgusting crufty way forced on us by the ()-less * definition of BEGIN. */ #define BEGIN yyg->yy_start = 1 + 2 * /* Translate the current start state into a value that can be later handed * to BEGIN to return to the state. The YYSTATE alias is for lex * compatibility. */ #define YY_START ((yyg->yy_start - 1) / 2) #define YYSTATE YY_START /* Action number for EOF rule of a given start state. */ #define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) /* Special action meaning "start processing a new file". */ #define YY_NEW_FILE ael_yyrestart(yyin ,yyscanner ) #define YY_END_OF_BUFFER_CHAR 0 /* Size of default input buffer. */ #ifndef YY_BUF_SIZE #define YY_BUF_SIZE 16384 #endif /* The state buf must be large enough to hold one state per character in the main buffer. */ #define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) #ifndef YY_TYPEDEF_YY_BUFFER_STATE #define YY_TYPEDEF_YY_BUFFER_STATE typedef struct yy_buffer_state *YY_BUFFER_STATE; #endif #define EOB_ACT_CONTINUE_SCAN 0 #define EOB_ACT_END_OF_FILE 1 #define EOB_ACT_LAST_MATCH 2 #define YY_LESS_LINENO(n) /* Return all but the first "n" matched characters back to the input stream. */ #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ *yy_cp = yyg->yy_hold_char; \ YY_RESTORE_YY_MORE_OFFSET \ yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ YY_DO_BEFORE_ACTION; /* set up yytext again */ \ } \ while ( 0 ) #define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner ) /* The following is because we cannot portably get our hands on size_t * (without autoconf's help, which isn't available because we want * flex-generated scanners to compile on their own). */ #ifndef YY_TYPEDEF_YY_SIZE_T #define YY_TYPEDEF_YY_SIZE_T typedef unsigned int yy_size_t; #endif #ifndef YY_STRUCT_YY_BUFFER_STATE #define YY_STRUCT_YY_BUFFER_STATE struct yy_buffer_state { FILE *yy_input_file; char *yy_ch_buf; /* input buffer */ char *yy_buf_pos; /* current position in input buffer */ /* Size of input buffer in bytes, not including room for EOB * characters. */ yy_size_t yy_buf_size; /* Number of characters read into yy_ch_buf, not including EOB * characters. */ int yy_n_chars; /* Whether we "own" the buffer - i.e., we know we created it, * and can realloc() it to grow it, and should free() it to * delete it. */ int yy_is_our_buffer; /* Whether this is an "interactive" input source; if so, and * if we're using stdio for input, then we want to use getc() * instead of fread(), to make sure we stop fetching input after * each newline. */ int yy_is_interactive; /* Whether we're considered to be at the beginning of a line. * If so, '^' rules will be active on the next match, otherwise * not. */ int yy_at_bol; int yy_bs_lineno; /**< The line count. */ int yy_bs_column; /**< The column count. */ /* Whether to try to fill the input buffer when we reach the * end of it. */ int yy_fill_buffer; int yy_buffer_status; #define YY_BUFFER_NEW 0 #define YY_BUFFER_NORMAL 1 /* When an EOF's been seen but there's still some text to process * then we mark the buffer as YY_EOF_PENDING, to indicate that we * shouldn't try reading from the input source any more. We might * still have a bunch of tokens to match, though, because of * possible backing-up. * * When we actually see the EOF, we change the status to "new" * (via ael_yyrestart()), so that the user can continue scanning by * just pointing yyin at a new input file. */ #define YY_BUFFER_EOF_PENDING 2 }; #endif /* !YY_STRUCT_YY_BUFFER_STATE */ /* We provide macros for accessing buffer states in case in the * future we want to put the buffer states in a more general * "scanner state". * * Returns the top of the stack, or NULL. */ #define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \ ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \ : NULL) /* Same as previous macro, but useful when we know that the buffer stack is not * NULL or when we need an lvalue. For internal use only. */ #define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] void ael_yyrestart (FILE *input_file ,yyscan_t yyscanner ); void ael_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); YY_BUFFER_STATE ael_yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner ); void ael_yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); void ael_yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); void ael_yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); void ael_yypop_buffer_state (yyscan_t yyscanner ); static void ael_yyensure_buffer_stack (yyscan_t yyscanner ); static void ael_yy_load_buffer_state (yyscan_t yyscanner ); static void ael_yy_init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscanner ); #define YY_FLUSH_BUFFER ael_yy_flush_buffer(YY_CURRENT_BUFFER ,yyscanner) YY_BUFFER_STATE ael_yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner ); YY_BUFFER_STATE ael_yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner ); YY_BUFFER_STATE ael_yy_scan_bytes (yyconst char *bytes,int len ,yyscan_t yyscanner ); void *ael_yyalloc (yy_size_t ,yyscan_t yyscanner ); void *ael_yyrealloc (void *,yy_size_t ,yyscan_t yyscanner ); void ael_yyfree (void * ,yyscan_t yyscanner ); #define yy_new_buffer ael_yy_create_buffer #define yy_set_interactive(is_interactive) \ { \ if ( ! YY_CURRENT_BUFFER ){ \ ael_yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ ael_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ } #define yy_set_bol(at_bol) \ { \ if ( ! YY_CURRENT_BUFFER ){\ ael_yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ ael_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ } #define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) /* Begin user sect3 */ #define ael_yywrap(n) 1 #define YY_SKIP_YYWRAP typedef unsigned char YY_CHAR; typedef int yy_state_type; #define yytext_ptr yytext_r static yy_state_type yy_get_previous_state (yyscan_t yyscanner ); static yy_state_type yy_try_NUL_trans (yy_state_type current_state ,yyscan_t yyscanner); static int yy_get_next_buffer (yyscan_t yyscanner ); static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner ); /* Done after the current pattern has been matched and before the * corresponding action - sets up yytext. */ #define YY_DO_BEFORE_ACTION \ yyg->yytext_ptr = yy_bp; \ yyg->yytext_ptr -= yyg->yy_more_len; \ yyleng = (size_t) (yy_cp - yyg->yytext_ptr); \ yyg->yy_hold_char = *yy_cp; \ *yy_cp = '\0'; \ yyg->yy_c_buf_p = yy_cp; #define YY_NUM_RULES 62 #define YY_END_OF_BUFFER 63 /* This struct is not used in this scanner, but its presence is necessary. */ struct yy_trans_info { flex_int32_t yy_verify; flex_int32_t yy_nxt; }; static yyconst flex_int16_t yy_accept[239] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 42, 63, 62, 49, 47, 48, 50, 50, 9, 3, 4, 7, 50, 8, 5, 6, 12, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 1, 10, 2, 62, 52, 51, 62, 53, 62, 58, 59, 60, 62, 62, 54, 55, 56, 62, 57, 42, 43, 44, 49, 48, 50, 50, 41, 13, 11, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 21, 50, 50, 50, 50, 50, 50, 50, 50, 50, 0, 52, 51, 0, 53, 52, 51, 53, 0, 58, 59, 60, 0, 58, 59, 60, 0, 54, 55, 56, 0, 57, 54, 55, 56, 57, 42, 43, 44, 45, 44, 46, 50, 13, 13, 50, 50, 50, 50, 50, 50, 50, 50, 50, 32, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 34, 50, 50, 50, 26, 50, 50, 50, 27, 25, 50, 50, 50, 28, 50, 50, 50, 50, 50, 50, 50, 50, 50, 30, 37, 50, 50, 50, 50, 50, 50, 50, 50, 50, 17, 50, 50, 50, 50, 50, 33, 50, 50, 50, 50, 50, 50, 16, 50, 22, 50, 50, 50, 23, 50, 29, 20, 50, 50, 14, 50, 35, 50, 18, 50, 50, 36, 50, 50, 50, 15, 31, 50, 50, 40, 24, 38, 0, 39, 19, 0, 0, 61, 0 } ; static yyconst flex_int32_t yy_ec[256] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 5, 6, 7, 5, 1, 8, 5, 9, 10, 11, 5, 12, 5, 5, 13, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 14, 15, 5, 16, 17, 1, 18, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 19, 5, 5, 5, 5, 5, 5, 20, 21, 22, 1, 5, 1, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 5, 39, 40, 41, 42, 5, 43, 44, 5, 5, 45, 46, 47, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } ; static yyconst flex_int32_t yy_meta[48] = { 0, 1, 1, 2, 1, 3, 4, 3, 1, 1, 1, 5, 1, 3, 1, 1, 1, 3, 1, 3, 3, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 3 } ; static yyconst flex_int16_t yy_base[252] = { 0, 0, 0, 39, 42, 81, 120, 159, 198, 47, 54, 315, 985, 312, 985, 309, 0, 281, 985, 985, 985, 985, 42, 985, 985, 294, 985, 286, 270, 31, 281, 32, 270, 33, 275, 45, 263, 281, 280, 48, 259, 271, 985, 985, 985, 73, 985, 985, 89, 985, 237, 985, 985, 985, 276, 315, 985, 985, 985, 354, 985, 297, 985, 66, 297, 291, 0, 258, 0, 401, 985, 253, 265, 64, 254, 261, 248, 244, 242, 242, 243, 238, 242, 258, 240, 250, 239, 248, 231, 235, 51, 239, 238, 103, 985, 985, 137, 985, 142, 176, 181, 439, 985, 985, 985, 478, 517, 556, 595, 634, 985, 985, 985, 673, 985, 712, 751, 790, 829, 265, 985, 103, 985, 104, 985, 242, 0, 876, 225, 242, 237, 238, 221, 238, 233, 225, 228, 0, 230, 216, 210, 219, 212, 214, 206, 203, 200, 214, 212, 196, 196, 202, 201, 195, 200, 0, 202, 100, 189, 0, 189, 193, 204, 0, 0, 190, 185, 180, 0, 180, 189, 178, 170, 174, 187, 184, 167, 182, 0, 0, 156, 163, 161, 169, 167, 158, 161, 156, 152, 0, 138, 141, 134, 137, 136, 0, 135, 135, 115, 113, 113, 123, 0, 109, 0, 107, 117, 107, 0, 112, 0, 111, 110, 92, 0, 105, 0, 95, 0, 85, 60, 0, 61, 48, 117, 0, 0, 45, 37, 0, 0, 0, 168, 0, 0, 0, 50, 985, 985, 922, 927, 932, 937, 940, 945, 950, 955, 960, 964, 969, 974, 979 } ; static yyconst flex_int16_t yy_def[252] = { 0, 238, 1, 239, 239, 240, 240, 241, 241, 242, 242, 238, 238, 238, 238, 238, 243, 243, 238, 238, 238, 238, 243, 238, 238, 238, 238, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 238, 238, 238, 244, 238, 238, 244, 238, 245, 238, 238, 238, 245, 246, 238, 238, 238, 246, 238, 247, 238, 248, 238, 238, 243, 243, 243, 249, 238, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 244, 238, 238, 244, 238, 244, 244, 244, 245, 238, 238, 238, 245, 245, 245, 245, 246, 238, 238, 238, 246, 238, 246, 246, 246, 246, 247, 238, 248, 238, 248, 238, 243, 250, 249, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 238, 243, 243, 251, 251, 238, 0, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238 } ; static yyconst flex_int16_t yy_nxt[1033] = { 0, 12, 13, 14, 15, 16, 16, 17, 18, 19, 20, 16, 21, 22, 23, 24, 25, 16, 26, 16, 16, 12, 16, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 16, 16, 37, 16, 16, 38, 39, 40, 16, 16, 41, 16, 42, 43, 44, 46, 47, 62, 46, 47, 68, 73, 69, 237, 62, 63, 46, 48, 49, 46, 48, 49, 63, 76, 80, 74, 122, 81, 89, 77, 83, 84, 90, 78, 123, 234, 124, 148, 85, 94, 95, 46, 233, 49, 46, 231, 49, 51, 52, 149, 94, 96, 97, 53, 230, 98, 99, 229, 51, 54, 52, 130, 131, 122, 122, 228, 98, 96, 100, 94, 95, 238, 123, 238, 238, 94, 232, 97, 232, 227, 94, 96, 97, 51, 180, 52, 51, 52, 181, 226, 225, 98, 53, 100, 224, 223, 222, 51, 54, 52, 221, 220, 219, 98, 99, 94, 218, 97, 94, 95, 217, 216, 215, 214, 98, 96, 100, 213, 212, 94, 96, 97, 51, 211, 52, 56, 57, 232, 58, 232, 210, 235, 209, 208, 207, 206, 56, 59, 60, 98, 205, 100, 94, 95, 94, 204, 97, 94, 95, 203, 202, 201, 200, 94, 96, 97, 199, 198, 94, 96, 97, 56, 197, 60, 56, 57, 196, 58, 195, 194, 193, 192, 191, 190, 189, 56, 59, 60, 94, 188, 97, 187, 186, 94, 185, 97, 184, 183, 182, 179, 178, 177, 176, 175, 174, 173, 172, 171, 170, 169, 56, 168, 60, 102, 103, 167, 166, 165, 164, 104, 163, 162, 161, 160, 102, 105, 103, 159, 158, 157, 156, 155, 154, 153, 152, 120, 151, 150, 147, 146, 145, 144, 143, 142, 141, 140, 139, 138, 137, 102, 136, 103, 106, 107, 135, 134, 133, 132, 108, 129, 128, 125, 65, 106, 105, 107, 64, 120, 92, 91, 88, 87, 86, 82, 79, 75, 72, 71, 70, 67, 65, 64, 238, 238, 238, 238, 238, 238, 106, 238, 107, 110, 111, 238, 112, 238, 238, 238, 238, 238, 238, 238, 110, 113, 114, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 110, 238, 114, 115, 116, 238, 117, 238, 238, 238, 238, 238, 238, 238, 115, 113, 118, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 115, 238, 118, 126, 126, 238, 126, 238, 238, 238, 126, 126, 126, 238, 126, 238, 126, 126, 126, 238, 126, 238, 238, 126, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 126, 102, 103, 238, 238, 238, 238, 104, 238, 238, 238, 238, 102, 105, 103, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 102, 238, 103, 106, 107, 238, 238, 238, 238, 108, 238, 238, 238, 238, 106, 105, 107, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 106, 238, 107, 102, 103, 238, 238, 238, 238, 104, 238, 238, 238, 238, 102, 105, 103, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 102, 238, 103, 102, 103, 238, 238, 238, 238, 104, 238, 238, 238, 238, 102, 105, 103, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 102, 238, 103, 102, 103, 238, 238, 238, 238, 104, 238, 238, 238, 238, 102, 105, 103, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 102, 238, 103, 110, 111, 238, 112, 238, 238, 238, 238, 238, 238, 238, 110, 113, 114, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 110, 238, 114, 115, 116, 238, 117, 238, 238, 238, 238, 238, 238, 238, 115, 113, 118, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 115, 238, 118, 110, 111, 238, 112, 238, 238, 238, 238, 238, 238, 238, 110, 113, 114, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 110, 238, 114, 110, 111, 238, 112, 238, 238, 238, 238, 238, 238, 238, 110, 113, 114, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 110, 238, 114, 110, 111, 238, 112, 238, 238, 238, 238, 238, 238, 238, 110, 113, 114, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 110, 238, 114, 110, 111, 238, 112, 238, 238, 238, 238, 238, 238, 238, 110, 113, 114, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 110, 238, 114, 126, 126, 238, 126, 238, 238, 238, 126, 126, 126, 238, 126, 238, 126, 126, 126, 238, 126, 238, 238, 126, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 126, 45, 45, 45, 45, 45, 50, 50, 50, 50, 50, 55, 55, 55, 55, 55, 61, 61, 61, 61, 61, 66, 66, 66, 93, 93, 93, 93, 93, 101, 101, 101, 101, 101, 109, 109, 109, 109, 109, 119, 119, 119, 119, 121, 121, 121, 121, 121, 127, 238, 127, 127, 127, 126, 238, 126, 126, 126, 236, 236, 236, 238, 236, 11, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238 } ; static yyconst flex_int16_t yy_chk[1033] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 9, 4, 4, 22, 29, 22, 236, 10, 9, 3, 3, 3, 4, 4, 4, 10, 31, 33, 29, 63, 33, 39, 31, 35, 35, 39, 31, 63, 228, 63, 90, 35, 45, 45, 3, 227, 3, 4, 223, 4, 5, 5, 90, 45, 45, 45, 5, 222, 48, 48, 220, 5, 5, 5, 73, 73, 121, 123, 219, 48, 48, 48, 93, 93, 121, 123, 121, 123, 45, 224, 45, 224, 217, 93, 93, 93, 5, 157, 5, 6, 6, 157, 215, 213, 48, 6, 48, 212, 211, 209, 6, 6, 6, 207, 206, 205, 96, 96, 93, 203, 93, 98, 98, 201, 200, 199, 198, 96, 96, 96, 197, 196, 98, 98, 98, 6, 194, 6, 7, 7, 232, 7, 232, 193, 232, 192, 191, 190, 188, 7, 7, 7, 96, 187, 96, 99, 99, 98, 186, 98, 100, 100, 185, 184, 183, 182, 99, 99, 99, 181, 180, 100, 100, 100, 7, 177, 7, 8, 8, 176, 8, 175, 174, 173, 172, 171, 170, 169, 8, 8, 8, 99, 167, 99, 166, 165, 100, 162, 100, 161, 160, 158, 156, 154, 153, 152, 151, 150, 149, 148, 147, 146, 145, 8, 144, 8, 50, 50, 143, 142, 141, 140, 50, 139, 138, 136, 135, 50, 50, 50, 134, 133, 132, 131, 130, 129, 128, 125, 119, 92, 91, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, 79, 50, 78, 50, 54, 54, 77, 76, 75, 74, 54, 72, 71, 67, 65, 54, 54, 54, 64, 61, 41, 40, 38, 37, 36, 34, 32, 30, 28, 27, 25, 17, 15, 13, 11, 0, 0, 0, 0, 0, 54, 0, 54, 55, 55, 0, 55, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 0, 55, 59, 59, 0, 59, 0, 0, 0, 0, 0, 0, 0, 59, 59, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59, 0, 59, 69, 69, 0, 69, 0, 0, 0, 69, 69, 69, 0, 69, 0, 69, 69, 69, 0, 69, 0, 0, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 101, 101, 0, 0, 0, 0, 101, 0, 0, 0, 0, 101, 101, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 101, 105, 105, 0, 0, 0, 0, 105, 0, 0, 0, 0, 105, 105, 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 105, 106, 106, 0, 0, 0, 0, 106, 0, 0, 0, 0, 106, 106, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 106, 0, 106, 107, 107, 0, 0, 0, 0, 107, 0, 0, 0, 0, 107, 107, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 107, 108, 108, 0, 0, 0, 0, 108, 0, 0, 0, 0, 108, 108, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 108, 0, 108, 109, 109, 0, 109, 0, 0, 0, 0, 0, 0, 0, 109, 109, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 109, 0, 109, 113, 113, 0, 113, 0, 0, 0, 0, 0, 0, 0, 113, 113, 113, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 113, 0, 113, 115, 115, 0, 115, 0, 0, 0, 0, 0, 0, 0, 115, 115, 115, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 115, 0, 115, 116, 116, 0, 116, 0, 0, 0, 0, 0, 0, 0, 116, 116, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 116, 0, 116, 117, 117, 0, 117, 0, 0, 0, 0, 0, 0, 0, 117, 117, 117, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 117, 0, 117, 118, 118, 0, 118, 0, 0, 0, 0, 0, 0, 0, 118, 118, 118, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 118, 0, 118, 127, 127, 0, 127, 0, 0, 0, 127, 127, 127, 0, 127, 0, 127, 127, 127, 0, 127, 0, 0, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 239, 239, 239, 239, 239, 240, 240, 240, 240, 240, 241, 241, 241, 241, 241, 242, 242, 242, 242, 242, 243, 243, 243, 244, 244, 244, 244, 244, 245, 245, 245, 245, 245, 246, 246, 246, 246, 246, 247, 247, 247, 247, 248, 248, 248, 248, 248, 249, 0, 249, 249, 249, 250, 0, 250, 250, 250, 251, 251, 251, 0, 251, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238 } ; /* The intent behind this definition is that it'll catch * any uses of REJECT which flex missed. */ #define REJECT reject_used_but_not_detected #define yymore() (yyg->yy_more_flag = 1) #define YY_MORE_ADJ yyg->yy_more_len #define YY_RESTORE_YY_MORE_OFFSET #line 1 "ael.flex" /* * Asterisk -- An open source telephony toolkit. * * Copyright (C) 2006, Digium, Inc. * * Steve Murphy * * 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 Flex scanner description of tokens used in AEL2 . * */ /* * Start with flex options: * * %x describes the contexts we have: paren, semic and argg, plus INITIAL */ /* prefix used for various globally-visible functions and variables. * This renames also ael_yywrap, but since we do not use it, we just * add option noyywrap to remove it. */ /* ael_yyfree normally just frees its arg. It can be null sometimes, which some systems will complain about, so, we'll define our own version */ /* batch gives a bit more performance if we are using it in * a non-interactive mode. We probably don't care much. */ /* outfile is the filename to be used instead of lex.yy.c */ /* * These are not supported in flex 2.5.4, but we need them * at the moment: * reentrant produces a thread-safe parser. Not 100% sure that * we require it, though. * bison-bridge passes an additional yylval argument to ael_yylex(). * bison-locations is probably not needed. */ #line 63 "ael.flex" ASTERISK_FILE_VERSION(__FILE__, "$Revision: 109309 $") #include #include #include #if defined(__Darwin__) || defined(__CYGWIN__) #define GLOB_ABORTED GLOB_ABEND #endif # include #include "asterisk/logger.h" #include "asterisk/utils.h" #include "ael/ael.tab.h" #include "asterisk/ael_structs.h" /* * A stack to keep track of matching brackets ( [ { } ] ) */ static char pbcstack[400]; /* XXX missing size checks */ static int pbcpos = 0; static void pbcpush(char x); static int pbcpop(char x); static int parencount = 0; /* * current line, column and filename, updated as we read the input. */ static int my_lineno = 1; /* current line in the source */ static int my_col = 1; /* current column in the source */ char *my_file = 0; /* used also in the bison code */ char *prev_word; /* XXX document it */ #define MAX_INCLUDE_DEPTH 50 /* * flex is not too smart, and generates global functions * without prototypes so the compiler may complain. * To avoid that, we declare the prototypes here, * even though these functions are not used. */ int ael_yyget_column (yyscan_t yyscanner); void ael_yyset_column (int column_no , yyscan_t yyscanner); int ael_yyparse (struct parse_io *); /* * A stack to process include files. * As we switch into the new file we need to store the previous * state to restore it later. */ struct stackelement { char *fname; int lineno; int colno; glob_t globbuf; /* the current globbuf */ int globbuf_pos; /* where we are in the current globbuf */ YY_BUFFER_STATE bufstate; }; static struct stackelement include_stack[MAX_INCLUDE_DEPTH]; static int include_stack_index = 0; static void setup_filestack(char *fnamebuf, int fnamebuf_siz, glob_t *globbuf, int globpos, yyscan_t xscan, int create); /* * if we use the @n feature of bison, we must supply the start/end * location of tokens in the structure pointed by yylloc. * Simple tokens are just assumed to be on the same line, so * the line number is constant, and the column is incremented * by the length of the token. */ #ifdef FLEX_BETA /* set for 2.5.33 */ /* compute the total number of lines and columns in the text * passed as argument. */ static void pbcwhere(const char *text, int *line, int *col ) { int loc_line = *line; int loc_col = *col; char c; while ( (c = *text++) ) { if ( c == '\t' ) { loc_col += 8 - (loc_col % 8); } else if ( c == '\n' ) { loc_line++; loc_col = 1; } else loc_col++; } *line = loc_line; *col = loc_col; } #define STORE_POS do { \ yylloc->first_line = yylloc->last_line = my_lineno; \ yylloc->first_column=my_col; \ yylloc->last_column=my_col+yyleng-1; \ my_col+=yyleng; \ } while (0) #define STORE_LOC do { \ yylloc->first_line = my_lineno; \ yylloc->first_column=my_col; \ pbcwhere(yytext, &my_lineno, &my_col); \ yylloc->last_line = my_lineno; \ yylloc->last_column = my_col - 1; \ } while (0) #else #define STORE_POS #define STORE_LOC #endif #line 908 "ael_lex.c" #define INITIAL 0 #define paren 1 #define semic 2 #define argg 3 #define comment 4 #ifndef YY_NO_UNISTD_H /* Special case for "unistd.h", since it is non-ANSI. We include it way * down here because we want the user's section 1 to have been scanned first. * The user has a chance to override it with an option. */ #include #endif #ifndef YY_EXTRA_TYPE #define YY_EXTRA_TYPE void * #endif /* Holds the entire state of the reentrant scanner. */ struct yyguts_t { /* User-defined. Not touched by flex. */ YY_EXTRA_TYPE yyextra_r; /* The rest are the same as the globals declared in the non-reentrant scanner. */ FILE *yyin_r, *yyout_r; size_t yy_buffer_stack_top; /**< index of top of stack. */ size_t yy_buffer_stack_max; /**< capacity of stack. */ YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */ char yy_hold_char; int yy_n_chars; int yyleng_r; char *yy_c_buf_p; int yy_init; int yy_start; int yy_did_buffer_switch_on_eof; int yy_start_stack_ptr; int yy_start_stack_depth; int *yy_start_stack; yy_state_type yy_last_accepting_state; char* yy_last_accepting_cpos; int yylineno_r; int yy_flex_debug_r; char *yytext_r; int yy_more_flag; int yy_more_len; YYSTYPE * yylval_r; YYLTYPE * yylloc_r; }; /* end struct yyguts_t */ static int yy_init_globals (yyscan_t yyscanner ); /* This must go here because YYSTYPE and YYLTYPE are included * from bison output in section 1.*/ # define yylval yyg->yylval_r # define yylloc yyg->yylloc_r /* Accessor methods to globals. These are made visible to non-reentrant scanners for convenience. */ int ael_yylex_destroy (yyscan_t yyscanner ); int ael_yyget_debug (yyscan_t yyscanner ); void ael_yyset_debug (int debug_flag ,yyscan_t yyscanner ); YY_EXTRA_TYPE ael_yyget_extra (yyscan_t yyscanner ); void ael_yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner ); FILE *ael_yyget_in (yyscan_t yyscanner ); void ael_yyset_in (FILE * in_str ,yyscan_t yyscanner ); FILE *ael_yyget_out (yyscan_t yyscanner ); void ael_yyset_out (FILE * out_str ,yyscan_t yyscanner ); int ael_yyget_leng (yyscan_t yyscanner ); char *ael_yyget_text (yyscan_t yyscanner ); int ael_yyget_lineno (yyscan_t yyscanner ); void ael_yyset_lineno (int line_number ,yyscan_t yyscanner ); YYSTYPE * ael_yyget_lval (yyscan_t yyscanner ); void ael_yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner ); YYLTYPE *ael_yyget_lloc (yyscan_t yyscanner ); void ael_yyset_lloc (YYLTYPE * yylloc_param ,yyscan_t yyscanner ); /* Macros after this point can all be overridden by user definitions in * section 1. */ #ifndef YY_SKIP_YYWRAP #ifdef __cplusplus extern "C" int ael_yywrap (yyscan_t yyscanner ); #else extern int ael_yywrap (yyscan_t yyscanner ); #endif #endif static void yyunput (int c,char *buf_ptr ,yyscan_t yyscanner); #ifndef yytext_ptr static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner); #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner); #endif #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (yyscan_t yyscanner ); #else static int input (yyscan_t yyscanner ); #endif #endif /* Amount of stuff to slurp up with each read. */ #ifndef YY_READ_BUF_SIZE #define YY_READ_BUF_SIZE 8192 #endif /* Copy whatever the last rule matched to the standard output. */ #ifndef ECHO /* This used to be an fputs(), but since the string might contain NUL's, * we now use fwrite(). */ #define ECHO (void) fwrite( yytext, yyleng, 1, yyout ) #endif /* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, * is returned in "result". */ #ifndef YY_INPUT #define YY_INPUT(buf,result,max_size) \ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ { \ int c = '*'; \ size_t n; \ for ( n = 0; n < max_size && \ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ buf[n] = (char) c; \ if ( c == '\n' ) \ buf[n++] = (char) c; \ if ( c == EOF && ferror( yyin ) ) \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ result = n; \ } \ else \ { \ errno=0; \ while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \ { \ if( errno != EINTR) \ { \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ break; \ } \ errno=0; \ clearerr(yyin); \ } \ }\ \ #endif /* No semi-colon after return; correct usage is to write "yyterminate();" - * we don't want an extra ';' after the "return" because that will cause * some compilers to complain about unreachable statements. */ #ifndef yyterminate #define yyterminate() return YY_NULL #endif /* Number of entries by which start-condition stack grows. */ #ifndef YY_START_STACK_INCR #define YY_START_STACK_INCR 25 #endif /* Report a fatal error. */ #ifndef YY_FATAL_ERROR #define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner) #endif /* end tables serialization structures and prototypes */ /* Default declaration of generated scanner - a define so the user can * easily add parameters. */ #ifndef YY_DECL #define YY_DECL_IS_OURS 1 extern int ael_yylex (YYSTYPE * yylval_param,YYLTYPE * yylloc_param ,yyscan_t yyscanner); #define YY_DECL int ael_yylex (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner) #endif /* !YY_DECL */ /* Code executed at the beginning of each rule, after yytext and yyleng * have been set up. */ #ifndef YY_USER_ACTION #define YY_USER_ACTION #endif /* Code executed at the end of each rule. */ #ifndef YY_BREAK #define YY_BREAK break; #endif #define YY_RULE_SETUP \ YY_USER_ACTION /** The main scanner function which does all the work. */ YY_DECL { register yy_state_type yy_current_state; register char *yy_cp, *yy_bp; register int yy_act; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; #line 185 "ael.flex" #line 1150 "ael_lex.c" yylval = yylval_param; yylloc = yylloc_param; if ( !yyg->yy_init ) { yyg->yy_init = 1; #ifdef YY_USER_INIT YY_USER_INIT; #endif if ( ! yyg->yy_start ) yyg->yy_start = 1; /* first start state */ if ( ! yyin ) yyin = stdin; if ( ! yyout ) yyout = stdout; if ( ! YY_CURRENT_BUFFER ) { ael_yyensure_buffer_stack (yyscanner); YY_CURRENT_BUFFER_LVALUE = ael_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); } ael_yy_load_buffer_state(yyscanner ); } while ( 1 ) /* loops until end-of-file is reached */ { yyg->yy_more_len = 0; if ( yyg->yy_more_flag ) { yyg->yy_more_len = yyg->yy_c_buf_p - yyg->yytext_ptr; yyg->yy_more_flag = 0; } yy_cp = yyg->yy_c_buf_p; /* Support of yytext. */ *yy_cp = yyg->yy_hold_char; /* yy_bp points to the position in yy_ch_buf of the start of * the current run. */ yy_bp = yy_cp; yy_current_state = yyg->yy_start; yy_match: do { register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; if ( yy_accept[yy_current_state] ) { yyg->yy_last_accepting_state = yy_current_state; yyg->yy_last_accepting_cpos = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 239 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; ++yy_cp; } while ( yy_current_state != 238 ); yy_cp = yyg->yy_last_accepting_cpos; yy_current_state = yyg->yy_last_accepting_state; yy_find_action: yy_act = yy_accept[yy_current_state]; YY_DO_BEFORE_ACTION; do_action: /* This label is used only to access EOF actions. */ switch ( yy_act ) { /* beginning of action switch */ case 0: /* must back up */ /* undo the effects of YY_DO_BEFORE_ACTION */ *yy_cp = yyg->yy_hold_char; yy_cp = yyg->yy_last_accepting_cpos; yy_current_state = yyg->yy_last_accepting_state; goto yy_find_action; case 1: YY_RULE_SETUP #line 187 "ael.flex" { STORE_POS; return LC;} YY_BREAK case 2: YY_RULE_SETUP #line 188 "ael.flex" { STORE_POS; return RC;} YY_BREAK case 3: YY_RULE_SETUP #line 189 "ael.flex" { STORE_POS; return LP;} YY_BREAK case 4: YY_RULE_SETUP #line 190 "ael.flex" { STORE_POS; return RP;} YY_BREAK case 5: YY_RULE_SETUP #line 191 "ael.flex" { STORE_POS; return SEMI;} YY_BREAK case 6: YY_RULE_SETUP #line 192 "ael.flex" { STORE_POS; return EQ;} YY_BREAK case 7: YY_RULE_SETUP #line 193 "ael.flex" { STORE_POS; return COMMA;} YY_BREAK case 8: YY_RULE_SETUP #line 194 "ael.flex" { STORE_POS; return COLON;} YY_BREAK case 9: YY_RULE_SETUP #line 195 "ael.flex" { STORE_POS; return AMPER;} YY_BREAK case 10: YY_RULE_SETUP #line 196 "ael.flex" { STORE_POS; return BAR;} YY_BREAK case 11: YY_RULE_SETUP #line 197 "ael.flex" { STORE_POS; return EXTENMARK;} YY_BREAK case 12: YY_RULE_SETUP #line 198 "ael.flex" { STORE_POS; return AT;} YY_BREAK case 13: YY_RULE_SETUP #line 199 "ael.flex" {/*comment*/} YY_BREAK case 14: YY_RULE_SETUP #line 200 "ael.flex" { STORE_POS; return KW_CONTEXT;} YY_BREAK case 15: YY_RULE_SETUP #line 201 "ael.flex" { STORE_POS; return KW_ABSTRACT;} YY_BREAK case 16: YY_RULE_SETUP #line 202 "ael.flex" { STORE_POS; return KW_EXTEND;} YY_BREAK case 17: YY_RULE_SETUP #line 203 "ael.flex" { STORE_POS; return KW_MACRO;}; YY_BREAK case 18: YY_RULE_SETUP #line 204 "ael.flex" { STORE_POS; return KW_GLOBALS;} YY_BREAK case 19: YY_RULE_SETUP #line 205 "ael.flex" { STORE_POS; return KW_IGNOREPAT;} YY_BREAK case 20: YY_RULE_SETUP #line 206 "ael.flex" { STORE_POS; return KW_SWITCH;} YY_BREAK case 21: YY_RULE_SETUP #line 207 "ael.flex" { STORE_POS; return KW_IF;} YY_BREAK case 22: YY_RULE_SETUP #line 208 "ael.flex" { STORE_POS; return KW_IFTIME;} YY_BREAK case 23: YY_RULE_SETUP #line 209 "ael.flex" { STORE_POS; return KW_RANDOM;} YY_BREAK case 24: YY_RULE_SETUP #line 210 "ael.flex" { STORE_POS; return KW_REGEXTEN;} YY_BREAK case 25: YY_RULE_SETUP #line 211 "ael.flex" { STORE_POS; return KW_HINT;} YY_BREAK case 26: YY_RULE_SETUP #line 212 "ael.flex" { STORE_POS; return KW_ELSE;} YY_BREAK case 27: YY_RULE_SETUP #line 213 "ael.flex" { STORE_POS; return KW_GOTO;} YY_BREAK case 28: YY_RULE_SETUP #line 214 "ael.flex" { STORE_POS; return KW_JUMP;} YY_BREAK case 29: YY_RULE_SETUP #line 215 "ael.flex" { STORE_POS; return KW_RETURN;} YY_BREAK case 30: YY_RULE_SETUP #line 216 "ael.flex" { STORE_POS; return KW_BREAK;} YY_BREAK case 31: YY_RULE_SETUP #line 217 "ael.flex" { STORE_POS; return KW_CONTINUE;} YY_BREAK case 32: YY_RULE_SETUP #line 218 "ael.flex" { STORE_POS; return KW_FOR;} YY_BREAK case 33: YY_RULE_SETUP #line 219 "ael.flex" { STORE_POS; return KW_WHILE;} YY_BREAK case 34: YY_RULE_SETUP #line 220 "ael.flex" { STORE_POS; return KW_CASE;} YY_BREAK case 35: YY_RULE_SETUP #line 221 "ael.flex" { STORE_POS; return KW_DEFAULT;} YY_BREAK case 36: YY_RULE_SETUP #line 222 "ael.flex" { STORE_POS; return KW_PATTERN;} YY_BREAK case 37: YY_RULE_SETUP #line 223 "ael.flex" { STORE_POS; return KW_CATCH;} YY_BREAK case 38: YY_RULE_SETUP #line 224 "ael.flex" { STORE_POS; return KW_SWITCHES;} YY_BREAK case 39: YY_RULE_SETUP #line 225 "ael.flex" { STORE_POS; return KW_ESWITCHES;} YY_BREAK case 40: YY_RULE_SETUP #line 226 "ael.flex" { STORE_POS; return KW_INCLUDES;} YY_BREAK case 41: YY_RULE_SETUP #line 227 "ael.flex" { BEGIN(comment); my_col += 2; } YY_BREAK case 42: YY_RULE_SETUP #line 229 "ael.flex" { my_col += yyleng; } YY_BREAK case 43: /* rule 43 can match eol */ YY_RULE_SETUP #line 230 "ael.flex" { ++my_lineno; my_col=1;} YY_BREAK case 44: YY_RULE_SETUP #line 231 "ael.flex" { my_col += yyleng; } YY_BREAK case 45: /* rule 45 can match eol */ YY_RULE_SETUP #line 232 "ael.flex" { ++my_lineno; my_col=1;} YY_BREAK case 46: YY_RULE_SETUP #line 233 "ael.flex" { my_col += 2; BEGIN(INITIAL); } YY_BREAK case 47: /* rule 47 can match eol */ YY_RULE_SETUP #line 235 "ael.flex" { my_lineno++; my_col = 1; } YY_BREAK case 48: YY_RULE_SETUP #line 236 "ael.flex" { my_col += yyleng; } YY_BREAK case 49: YY_RULE_SETUP #line 237 "ael.flex" { my_col += (yyleng*8)-(my_col%8); } YY_BREAK case 50: YY_RULE_SETUP #line 239 "ael.flex" { STORE_POS; yylval->str = strdup(yytext); prev_word = yylval->str; return word; } YY_BREAK /* * context used for arguments of if_head, random_head, switch_head, * for (last statement), while (XXX why not iftime_head ?). * End with the matching parentheses. * A comma at the top level is valid here, unlike in argg where it * is an argument separator so it must be returned as a token. */ case 51: /* rule 51 can match eol */ YY_RULE_SETUP #line 255 "ael.flex" { if ( pbcpop(')') ) { /* error */ STORE_LOC; ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Mismatched ')' in expression: %s !\n", my_file, my_lineno, my_col, yytext); BEGIN(0); yylval->str = strdup(yytext); prev_word = 0; return word; } parencount--; if ( parencount >= 0) { yymore(); } else { STORE_LOC; yylval->str = strdup(yytext); yylval->str[yyleng-1] = '\0'; /* trim trailing ')' */ unput(')'); BEGIN(0); return word; } } YY_BREAK case 52: /* rule 52 can match eol */ YY_RULE_SETUP #line 277 "ael.flex" { char c = yytext[yyleng-1]; if (c == '(') parencount++; pbcpush(c); yymore(); } YY_BREAK case 53: /* rule 53 can match eol */ YY_RULE_SETUP #line 285 "ael.flex" { char c = yytext[yyleng-1]; if ( pbcpop(c)) { /* error */ STORE_LOC; ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Mismatched '%c' in expression!\n", my_file, my_lineno, my_col, c); BEGIN(0); yylval->str = strdup(yytext); return word; } yymore(); } YY_BREAK /* * handlers for arguments to a macro or application calls. * We enter this context when we find the initial '(' and * stay here until we close all matching parentheses, * and find the comma (argument separator) or the closing ')' * of the (external) call, which happens when parencount == 0 * before the decrement. */ case 54: /* rule 54 can match eol */ YY_RULE_SETUP #line 307 "ael.flex" { char c = yytext[yyleng-1]; if (c == '(') parencount++; pbcpush(c); yymore(); } YY_BREAK case 55: /* rule 55 can match eol */ YY_RULE_SETUP #line 315 "ael.flex" { if ( pbcpop(')') ) { /* error */ STORE_LOC; ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Mismatched ')' in expression!\n", my_file, my_lineno, my_col); BEGIN(0); yylval->str = strdup(yytext); return word; } parencount--; if( parencount >= 0){ yymore(); } else { STORE_LOC; BEGIN(0); if ( !strcmp(yytext, ")") ) return RP; yylval->str = strdup(yytext); yylval->str[yyleng-1] = '\0'; /* trim trailing ')' */ unput(')'); return word; } } YY_BREAK case 56: /* rule 56 can match eol */ YY_RULE_SETUP #line 339 "ael.flex" { if( parencount != 0) { /* printf("Folding in a comma!\n"); */ yymore(); } else { STORE_LOC; if( !strcmp(yytext,"," ) ) return COMMA; yylval->str = strdup(yytext); yylval->str[yyleng-1] = '\0'; unput(','); return word; } } YY_BREAK case 57: /* rule 57 can match eol */ YY_RULE_SETUP #line 353 "ael.flex" { char c = yytext[yyleng-1]; if ( pbcpop(c) ) { /* error */ STORE_LOC; ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Mismatched '%c' in expression!\n", my_file, my_lineno, my_col, c); BEGIN(0); yylval->str = strdup(yytext); return word; } yymore(); } YY_BREAK /* * context used to find tokens in the right hand side of assignments, * or in the first and second operand of a 'for'. As above, match * commas and use ';' as a separator (hence return it as a separate token). */ case 58: /* rule 58 can match eol */ YY_RULE_SETUP #line 370 "ael.flex" { char c = yytext[yyleng-1]; yymore(); pbcpush(c); } YY_BREAK case 59: /* rule 59 can match eol */ YY_RULE_SETUP #line 376 "ael.flex" { char c = yytext[yyleng-1]; if ( pbcpop(c) ) { /* error */ STORE_LOC; ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Mismatched '%c' in expression!\n", my_file, my_lineno, my_col, c); BEGIN(0); yylval->str = strdup(yytext); return word; } yymore(); } YY_BREAK case 60: /* rule 60 can match eol */ YY_RULE_SETUP #line 388 "ael.flex" { STORE_LOC; yylval->str = strdup(yytext); yylval->str[yyleng-1] = '\0'; unput(';'); BEGIN(0); return word; } YY_BREAK case 61: /* rule 61 can match eol */ YY_RULE_SETUP #line 397 "ael.flex" { char fnamebuf[1024],*p1,*p2; int glob_ret; glob_t globbuf; /* the current globbuf */ int globbuf_pos = -1; /* where we are in the current globbuf */ globbuf.gl_offs = 0; /* initialize it to silence gcc */ p1 = strchr(yytext,'"'); p2 = strrchr(yytext,'"'); if ( include_stack_index >= MAX_INCLUDE_DEPTH ) { ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Includes nested too deeply! Wow!!! How did you do that?\n", my_file, my_lineno, my_col); } else if ( (int)(p2-p1) > sizeof(fnamebuf) - 1 ) { ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Filename is incredibly way too long (%d chars!). Inclusion ignored!\n", my_file, my_lineno, my_col, yyleng - 10); } else { strncpy(fnamebuf, p1+1, p2-p1-1); fnamebuf[p2-p1-1] = 0; #ifdef SOLARIS glob_ret = glob(fnamebuf, GLOB_NOCHECK, NULL, &globbuf); #else glob_ret = glob(fnamebuf, 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", fnamebuf); } else if (glob_ret == GLOB_ABORTED) { ast_log(LOG_WARNING, "Glob Expansion of pattern '%s' failed: Read error\n", fnamebuf); } else if (glob_ret == GLOB_NOMATCH) { ast_log(LOG_WARNING, "Glob Expansion of pattern '%s' failed: No matches!\n", fnamebuf); } else { globbuf_pos = 0; } } if (globbuf_pos > -1) { setup_filestack(fnamebuf, sizeof(fnamebuf), &globbuf, 0, yyscanner, 1); } } YY_BREAK case YY_STATE_EOF(INITIAL): case YY_STATE_EOF(paren): case YY_STATE_EOF(semic): case YY_STATE_EOF(argg): case YY_STATE_EOF(comment): #line 438 "ael.flex" { char fnamebuf[2048]; if (include_stack_index > 0 && include_stack[include_stack_index-1].globbuf_pos < include_stack[include_stack_index-1].globbuf.gl_pathc-1) { ael_yy_delete_buffer(YY_CURRENT_BUFFER,yyscanner ); include_stack[include_stack_index-1].globbuf_pos++; setup_filestack(fnamebuf, sizeof(fnamebuf), &include_stack[include_stack_index-1].globbuf, include_stack[include_stack_index-1].globbuf_pos, yyscanner, 0); /* finish this */ } else { if (include_stack[include_stack_index].fname) { free(include_stack[include_stack_index].fname); include_stack[include_stack_index].fname = 0; } if (my_file) { free(my_file); my_file = 0; } if ( --include_stack_index < 0 ) { yyterminate(); } else { globfree(&include_stack[include_stack_index].globbuf); include_stack[include_stack_index].globbuf_pos = -1; ael_yy_delete_buffer(YY_CURRENT_BUFFER,yyscanner ); ael_yy_switch_to_buffer(include_stack[include_stack_index].bufstate,yyscanner ); my_lineno = include_stack[include_stack_index].lineno; my_col = include_stack[include_stack_index].colno; my_file = strdup(include_stack[include_stack_index].fname); } } } YY_BREAK case 62: YY_RULE_SETUP #line 470 "ael.flex" ECHO; YY_BREAK #line 1772 "ael_lex.c" case YY_END_OF_BUFFER: { /* Amount of text matched not including the EOB char. */ int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1; /* Undo the effects of YY_DO_BEFORE_ACTION. */ *yy_cp = yyg->yy_hold_char; YY_RESTORE_YY_MORE_OFFSET if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) { /* We're scanning a new file or input source. It's * possible that this happened because the user * just pointed yyin at a new source and called * ael_yylex(). If so, then we have to assure * consistency between YY_CURRENT_BUFFER and our * globals. Here is the right place to do so, because * this is the first action (other than possibly a * back-up) that will match for the new input source. */ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; } /* Note that here we test for yy_c_buf_p "<=" to the position * of the first EOB in the buffer, since yy_c_buf_p will * already have been incremented past the NUL character * (since all states make transitions on EOB to the * end-of-buffer state). Contrast this with the test * in input(). */ if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) { /* This was really a NUL. */ yy_state_type yy_next_state; yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( yyscanner ); /* Okay, we're now positioned to make the NUL * transition. We couldn't have * yy_get_previous_state() go ahead and do it * for us because it doesn't know how to deal * with the possibility of jamming (and we don't * want to build jamming into it because then it * will run more slowly). */ yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; if ( yy_next_state ) { /* Consume the NUL. */ yy_cp = ++yyg->yy_c_buf_p; yy_current_state = yy_next_state; goto yy_match; } else { yy_cp = yyg->yy_last_accepting_cpos; yy_current_state = yyg->yy_last_accepting_state; goto yy_find_action; } } else switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_END_OF_FILE: { yyg->yy_did_buffer_switch_on_eof = 0; if ( ael_yywrap(yyscanner ) ) { /* Note: because we've taken care in * yy_get_next_buffer() to have set up * yytext, we can now set up * yy_c_buf_p so that if some total * hoser (like flex itself) wants to * call the scanner after we return the * YY_NULL, it'll still work - another * YY_NULL will get returned. */ yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; yy_act = YY_STATE_EOF(YY_START); goto do_action; } else { if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; } break; } case EOB_ACT_CONTINUE_SCAN: yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( yyscanner ); yy_cp = yyg->yy_c_buf_p; yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_match; case EOB_ACT_LAST_MATCH: yyg->yy_c_buf_p = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; yy_current_state = yy_get_previous_state( yyscanner ); yy_cp = yyg->yy_c_buf_p; yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_find_action; } break; } default: YY_FATAL_ERROR( "fatal flex scanner internal error--no action found" ); } /* end of action switch */ } /* end of scanning one token */ } /* end of ael_yylex */ /* yy_get_next_buffer - try to read in a new buffer * * Returns a code representing an action: * EOB_ACT_LAST_MATCH - * EOB_ACT_CONTINUE_SCAN - continue scanning from current position * EOB_ACT_END_OF_FILE - end of file */ static int yy_get_next_buffer (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; register char *source = yyg->yytext_ptr; register int number_to_move, i; int ret_val; if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] ) YY_FATAL_ERROR( "fatal flex scanner internal error--end of buffer missed" ); if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) { /* Don't try to fill the buffer, so this is an EOF. */ if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 ) { /* We matched a single character, the EOB, so * treat this as a final EOF. */ return EOB_ACT_END_OF_FILE; } else { /* We matched some text prior to the EOB, first * process it. */ return EOB_ACT_LAST_MATCH; } } /* Try to read more data. */ /* First move last chars to start of buffer. */ number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1; for ( i = 0; i < number_to_move; ++i ) *(dest++) = *(source++); if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) /* don't do the read, it's not guaranteed to return an EOF, * just force an EOF */ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; else { int num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; while ( num_to_read <= 0 ) { /* Not enough room in the buffer - grow it. */ /* just a shorter name for the current buffer */ YY_BUFFER_STATE b = YY_CURRENT_BUFFER; int yy_c_buf_p_offset = (int) (yyg->yy_c_buf_p - b->yy_ch_buf); if ( b->yy_is_our_buffer ) { int new_size = b->yy_buf_size * 2; if ( new_size <= 0 ) b->yy_buf_size += b->yy_buf_size / 8; else b->yy_buf_size *= 2; b->yy_ch_buf = (char *) /* Include room in for 2 EOB chars. */ ael_yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ,yyscanner ); } else /* Can't grow it, we don't own it. */ b->yy_ch_buf = 0; if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; } if ( num_to_read > YY_READ_BUF_SIZE ) num_to_read = YY_READ_BUF_SIZE; /* Read in more data. */ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), yyg->yy_n_chars, num_to_read ); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } if ( yyg->yy_n_chars == 0 ) { if ( number_to_move == YY_MORE_ADJ ) { ret_val = EOB_ACT_END_OF_FILE; ael_yyrestart(yyin ,yyscanner); } else { ret_val = EOB_ACT_LAST_MATCH; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_EOF_PENDING; } } else ret_val = EOB_ACT_CONTINUE_SCAN; yyg->yy_n_chars += number_to_move; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; return ret_val; } /* yy_get_previous_state - get the state just before the EOB char was reached */ static yy_state_type yy_get_previous_state (yyscan_t yyscanner) { register yy_state_type yy_current_state; register char *yy_cp; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yy_current_state = yyg->yy_start; for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) { register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); if ( yy_accept[yy_current_state] ) { yyg->yy_last_accepting_state = yy_current_state; yyg->yy_last_accepting_cpos = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 239 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; } return yy_current_state; } /* yy_try_NUL_trans - try to make a transition on the NUL character * * synopsis * next_state = yy_try_NUL_trans( current_state ); */ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) { register int yy_is_jam; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ register char *yy_cp = yyg->yy_c_buf_p; register YY_CHAR yy_c = 1; if ( yy_accept[yy_current_state] ) { yyg->yy_last_accepting_state = yy_current_state; yyg->yy_last_accepting_cpos = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 239 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; yy_is_jam = (yy_current_state == 238); return yy_is_jam ? 0 : yy_current_state; } static void yyunput (int c, register char * yy_bp , yyscan_t yyscanner) { register char *yy_cp; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yy_cp = yyg->yy_c_buf_p; /* undo effects of setting up yytext */ *yy_cp = yyg->yy_hold_char; if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) { /* need to shift things up to make room */ /* +2 for EOB chars. */ register int number_to_move = yyg->yy_n_chars + 2; register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; register char *source = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) *--dest = *--source; yy_cp += (int) (dest - source); yy_bp += (int) (dest - source); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_buf_size; if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) YY_FATAL_ERROR( "flex scanner push-back overflow" ); } *--yy_cp = (char) c; yyg->yytext_ptr = yy_bp; yyg->yy_hold_char = *yy_cp; yyg->yy_c_buf_p = yy_cp; } #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (yyscan_t yyscanner) #else static int input (yyscan_t yyscanner) #endif { int c; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; *yyg->yy_c_buf_p = yyg->yy_hold_char; if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) { /* yy_c_buf_p now points to the character we want to return. * If this occurs *before* the EOB characters, then it's a * valid NUL; if not, then we've hit the end of the buffer. */ if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) /* This was really a NUL. */ *yyg->yy_c_buf_p = '\0'; else { /* need more input */ int offset = yyg->yy_c_buf_p - yyg->yytext_ptr; ++yyg->yy_c_buf_p; switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_LAST_MATCH: /* This happens because yy_g_n_b() * sees that we've accumulated a * token and flags that we need to * try matching the token before * proceeding. But for input(), * there's no matching to consider. * So convert the EOB_ACT_LAST_MATCH * to EOB_ACT_END_OF_FILE. */ /* Reset buffer status. */ ael_yyrestart(yyin ,yyscanner); /*FALLTHROUGH*/ case EOB_ACT_END_OF_FILE: { if ( ael_yywrap(yyscanner ) ) return EOF; if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; #ifdef __cplusplus return yyinput(yyscanner); #else return input(yyscanner); #endif } case EOB_ACT_CONTINUE_SCAN: yyg->yy_c_buf_p = yyg->yytext_ptr + offset; break; } } } c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */ *yyg->yy_c_buf_p = '\0'; /* preserve yytext */ yyg->yy_hold_char = *++yyg->yy_c_buf_p; return c; } #endif /* ifndef YY_NO_INPUT */ /** Immediately switch to a different input stream. * @param input_file A readable stream. * @param yyscanner The scanner object. * @note This function does not reset the start condition to @c INITIAL . */ void ael_yyrestart (FILE * input_file , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! YY_CURRENT_BUFFER ){ ael_yyensure_buffer_stack (yyscanner); YY_CURRENT_BUFFER_LVALUE = ael_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); } ael_yy_init_buffer(YY_CURRENT_BUFFER,input_file ,yyscanner); ael_yy_load_buffer_state(yyscanner ); } /** Switch to a different input buffer. * @param new_buffer The new input buffer. * @param yyscanner The scanner object. */ void ael_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* TODO. We should be able to replace this entire function body * with * ael_yypop_buffer_state(); * ael_yypush_buffer_state(new_buffer); */ ael_yyensure_buffer_stack (yyscanner); if ( YY_CURRENT_BUFFER == new_buffer ) return; if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *yyg->yy_c_buf_p = yyg->yy_hold_char; YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } YY_CURRENT_BUFFER_LVALUE = new_buffer; ael_yy_load_buffer_state(yyscanner ); /* We don't actually know whether we did this switch during * EOF (ael_yywrap()) processing, but the only time this flag * is looked at is after ael_yywrap() is called, so it's safe * to go ahead and always set it. */ yyg->yy_did_buffer_switch_on_eof = 1; } static void ael_yy_load_buffer_state (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; yyg->yy_hold_char = *yyg->yy_c_buf_p; } /** Allocate and initialize an input buffer state. * @param file A readable stream. * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. * @param yyscanner The scanner object. * @return the allocated buffer state. */ YY_BUFFER_STATE ael_yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) { YY_BUFFER_STATE b; b = (YY_BUFFER_STATE) ael_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in ael_yy_create_buffer()" ); b->yy_buf_size = size; /* yy_ch_buf has to be 2 characters longer than the size given because * we need to put in 2 end-of-buffer characters. */ b->yy_ch_buf = (char *) ael_yyalloc(b->yy_buf_size + 2 ,yyscanner ); if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in ael_yy_create_buffer()" ); b->yy_is_our_buffer = 1; ael_yy_init_buffer(b,file ,yyscanner); return b; } /** Destroy the buffer. * @param b a buffer created with ael_yy_create_buffer() * @param yyscanner The scanner object. */ void ael_yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! b ) return; if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; if ( b->yy_is_our_buffer ) ael_yyfree((void *) b->yy_ch_buf ,yyscanner ); ael_yyfree((void *) b ,yyscanner ); } #ifndef __cplusplus extern int isatty (int ); #endif /* __cplusplus */ /* Initializes or reinitializes a buffer. * This function is sometimes called more than once on the same buffer, * such as during a ael_yyrestart() or at EOF. */ static void ael_yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) { int oerrno = errno; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; ael_yy_flush_buffer(b ,yyscanner); b->yy_input_file = file; b->yy_fill_buffer = 1; /* If b is the current buffer, then ael_yy_init_buffer was _probably_ * called from ael_yyrestart() or through yy_get_next_buffer. * In that case, we don't want to reset the lineno or column. */ if (b != YY_CURRENT_BUFFER){ b->yy_bs_lineno = 1; b->yy_bs_column = 0; } b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; errno = oerrno; } /** Discard all buffered characters. On the next scan, YY_INPUT will be called. * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. * @param yyscanner The scanner object. */ void ael_yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! b ) return; b->yy_n_chars = 0; /* We always need two end-of-buffer characters. The first causes * a transition to the end-of-buffer state. The second causes * a jam in that state. */ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; b->yy_buf_pos = &b->yy_ch_buf[0]; b->yy_at_bol = 1; b->yy_buffer_status = YY_BUFFER_NEW; if ( b == YY_CURRENT_BUFFER ) ael_yy_load_buffer_state(yyscanner ); } /** Pushes the new state onto the stack. The new state becomes * the current state. This function will allocate the stack * if necessary. * @param new_buffer The new state. * @param yyscanner The scanner object. */ void ael_yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (new_buffer == NULL) return; ael_yyensure_buffer_stack(yyscanner); /* This block is copied from ael_yy_switch_to_buffer. */ if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *yyg->yy_c_buf_p = yyg->yy_hold_char; YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } /* Only push if top exists. Otherwise, replace top. */ if (YY_CURRENT_BUFFER) yyg->yy_buffer_stack_top++; YY_CURRENT_BUFFER_LVALUE = new_buffer; /* copied from ael_yy_switch_to_buffer. */ ael_yy_load_buffer_state(yyscanner ); yyg->yy_did_buffer_switch_on_eof = 1; } /** Removes and deletes the top of the stack, if present. * The next element becomes the new top. * @param yyscanner The scanner object. */ void ael_yypop_buffer_state (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (!YY_CURRENT_BUFFER) return; ael_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner); YY_CURRENT_BUFFER_LVALUE = NULL; if (yyg->yy_buffer_stack_top > 0) --yyg->yy_buffer_stack_top; if (YY_CURRENT_BUFFER) { ael_yy_load_buffer_state(yyscanner ); yyg->yy_did_buffer_switch_on_eof = 1; } } /* Allocates the stack if it does not exist. * Guarantees space for at least one push. */ static void ael_yyensure_buffer_stack (yyscan_t yyscanner) { int num_to_alloc; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (!yyg->yy_buffer_stack) { /* First allocation is just for 2 elements, since we don't know if this * scanner will even need a stack. We use 2 instead of 1 to avoid an * immediate realloc on the next call. */ num_to_alloc = 1; yyg->yy_buffer_stack = (struct yy_buffer_state**)ael_yyalloc (num_to_alloc * sizeof(struct yy_buffer_state*) , yyscanner); memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*)); yyg->yy_buffer_stack_max = num_to_alloc; yyg->yy_buffer_stack_top = 0; return; } if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ /* Increase the buffer to prepare for a possible push. */ int grow_size = 8 /* arbitrary grow size */; num_to_alloc = yyg->yy_buffer_stack_max + grow_size; yyg->yy_buffer_stack = (struct yy_buffer_state**)ael_yyrealloc (yyg->yy_buffer_stack, num_to_alloc * sizeof(struct yy_buffer_state*) , yyscanner); /* zero only the new slots.*/ memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*)); yyg->yy_buffer_stack_max = num_to_alloc; } } /** Setup the input buffer state to scan directly from a user-specified character buffer. * @param base the character buffer * @param size the size in bytes of the character buffer * @param yyscanner The scanner object. * @return the newly allocated buffer state object. */ YY_BUFFER_STATE ael_yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) { YY_BUFFER_STATE b; if ( size < 2 || base[size-2] != YY_END_OF_BUFFER_CHAR || base[size-1] != YY_END_OF_BUFFER_CHAR ) /* They forgot to leave room for the EOB's. */ return 0; b = (YY_BUFFER_STATE) ael_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in ael_yy_scan_buffer()" ); b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ b->yy_buf_pos = b->yy_ch_buf = base; b->yy_is_our_buffer = 0; b->yy_input_file = 0; b->yy_n_chars = b->yy_buf_size; b->yy_is_interactive = 0; b->yy_at_bol = 1; b->yy_fill_buffer = 0; b->yy_buffer_status = YY_BUFFER_NEW; ael_yy_switch_to_buffer(b ,yyscanner ); return b; } /** Setup the input buffer state to scan a string. The next call to ael_yylex() will * scan from a @e copy of @a str. * @param str a NUL-terminated string to scan * @param yyscanner The scanner object. * @return the newly allocated buffer state object. * @note If you want to scan bytes that may contain NUL values, then use * ael_yy_scan_bytes() instead. */ YY_BUFFER_STATE ael_yy_scan_string (yyconst char * yystr , yyscan_t yyscanner) { return ael_yy_scan_bytes(yystr,strlen(yystr) ,yyscanner); } /** Setup the input buffer state to scan the given bytes. The next call to ael_yylex() will * scan from a @e copy of @a bytes. * @param bytes the byte buffer to scan * @param len the number of bytes in the buffer pointed to by @a bytes. * @param yyscanner The scanner object. * @return the newly allocated buffer state object. */ YY_BUFFER_STATE ael_yy_scan_bytes (yyconst char * yybytes, int _yybytes_len , yyscan_t yyscanner) { YY_BUFFER_STATE b; char *buf; yy_size_t n; int i; /* Get memory for full buffer, including space for trailing EOB's. */ n = _yybytes_len + 2; buf = (char *) ael_yyalloc(n ,yyscanner ); if ( ! buf ) YY_FATAL_ERROR( "out of dynamic memory in ael_yy_scan_bytes()" ); for ( i = 0; i < _yybytes_len; ++i ) buf[i] = yybytes[i]; buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; b = ael_yy_scan_buffer(buf,n ,yyscanner); if ( ! b ) YY_FATAL_ERROR( "bad buffer in ael_yy_scan_bytes()" ); /* It's okay to grow etc. this buffer, and we should throw it * away when we're done. */ b->yy_is_our_buffer = 1; return b; } #ifndef YY_EXIT_FAILURE #define YY_EXIT_FAILURE 2 #endif static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner) { (void) fprintf( stderr, "%s\n", msg ); exit( YY_EXIT_FAILURE ); } /* Redefine yyless() so it works in section 3 code. */ #undef yyless #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ yytext[yyleng] = yyg->yy_hold_char; \ yyg->yy_c_buf_p = yytext + yyless_macro_arg; \ yyg->yy_hold_char = *yyg->yy_c_buf_p; \ *yyg->yy_c_buf_p = '\0'; \ yyleng = yyless_macro_arg; \ } \ while ( 0 ) /* Accessor methods (get/set functions) to struct members. */ /** Get the user-defined data for this scanner. * @param yyscanner The scanner object. */ YY_EXTRA_TYPE ael_yyget_extra (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyextra; } /** Get the current line number. * @param yyscanner The scanner object. */ int ael_yyget_lineno (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (! YY_CURRENT_BUFFER) return 0; return yylineno; } /** Get the current column number. * @param yyscanner The scanner object. */ int ael_yyget_column (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (! YY_CURRENT_BUFFER) return 0; return yycolumn; } /** Get the input stream. * @param yyscanner The scanner object. */ FILE *ael_yyget_in (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyin; } /** Get the output stream. * @param yyscanner The scanner object. */ FILE *ael_yyget_out (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyout; } /** Get the length of the current token. * @param yyscanner The scanner object. */ int ael_yyget_leng (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyleng; } /** Get the current token. * @param yyscanner The scanner object. */ char *ael_yyget_text (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yytext; } /** Set the user-defined data. This data is never touched by the scanner. * @param user_defined The data to be associated with this scanner. * @param yyscanner The scanner object. */ void ael_yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyextra = user_defined ; } /** Set the current line number. * @param line_number * @param yyscanner The scanner object. */ void ael_yyset_lineno (int line_number , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* lineno is only valid if an input buffer exists. */ if (! YY_CURRENT_BUFFER ) yy_fatal_error( "ael_yyset_lineno called with no buffer" , yyscanner); yylineno = line_number; } /** Set the current column. * @param line_number * @param yyscanner The scanner object. */ void ael_yyset_column (int column_no , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* column is only valid if an input buffer exists. */ if (! YY_CURRENT_BUFFER ) yy_fatal_error( "ael_yyset_column called with no buffer" , yyscanner); yycolumn = column_no; } /** Set the input stream. This does not discard the current * input buffer. * @param in_str A readable stream. * @param yyscanner The scanner object. * @see ael_yy_switch_to_buffer */ void ael_yyset_in (FILE * in_str , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyin = in_str ; } void ael_yyset_out (FILE * out_str , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyout = out_str ; } int ael_yyget_debug (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yy_flex_debug; } void ael_yyset_debug (int bdebug , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yy_flex_debug = bdebug ; } /* Accessor methods for yylval and yylloc */ YYSTYPE * ael_yyget_lval (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yylval; } void ael_yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yylval = yylval_param; } YYLTYPE *ael_yyget_lloc (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yylloc; } void ael_yyset_lloc (YYLTYPE * yylloc_param , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yylloc = yylloc_param; } /* User-visible API */ /* ael_yylex_init is special because it creates the scanner itself, so it is * the ONLY reentrant function that doesn't take the scanner as the last argument. * That's why we explicitly handle the declaration, instead of using our macros. */ int ael_yylex_init(yyscan_t* ptr_yy_globals) { if (ptr_yy_globals == NULL){ errno = EINVAL; return 1; } *ptr_yy_globals = (yyscan_t) ael_yyalloc ( sizeof( struct yyguts_t ), NULL ); if (*ptr_yy_globals == NULL){ errno = ENOMEM; return 1; } /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); return yy_init_globals ( *ptr_yy_globals ); } static int yy_init_globals (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* Initialization is the same as for the non-reentrant scanner. * This function is called from ael_yylex_destroy(), so don't allocate here. */ yyg->yy_buffer_stack = 0; yyg->yy_buffer_stack_top = 0; yyg->yy_buffer_stack_max = 0; yyg->yy_c_buf_p = (char *) 0; yyg->yy_init = 0; yyg->yy_start = 0; yyg->yy_start_stack_ptr = 0; yyg->yy_start_stack_depth = 0; yyg->yy_start_stack = NULL; /* Defined in main.c */ #ifdef YY_STDINIT yyin = stdin; yyout = stdout; #else yyin = (FILE *) 0; yyout = (FILE *) 0; #endif /* For future reference: Set errno on error, since we are called by * ael_yylex_init() */ return 0; } /* ael_yylex_destroy is for both reentrant and non-reentrant scanners. */ int ael_yylex_destroy (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* Pop the buffer stack, destroying each element. */ while(YY_CURRENT_BUFFER){ ael_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner ); YY_CURRENT_BUFFER_LVALUE = NULL; ael_yypop_buffer_state(yyscanner); } /* Destroy the stack itself. */ ael_yyfree(yyg->yy_buffer_stack ,yyscanner); yyg->yy_buffer_stack = NULL; /* Destroy the start condition stack. */ ael_yyfree(yyg->yy_start_stack ,yyscanner ); yyg->yy_start_stack = NULL; /* Reset the globals. This is important in a non-reentrant scanner so the next time * ael_yylex() is called, initialization will occur. */ yy_init_globals( yyscanner); /* Destroy the main struct (reentrant only). */ ael_yyfree ( yyscanner , yyscanner ); yyscanner = NULL; return 0; } /* * Internal utility routines. */ #ifndef yytext_ptr static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner) { register int i; for ( i = 0; i < n; ++i ) s1[i] = s2[i]; } #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner) { register int n; for ( n = 0; s[n]; ++n ) ; return n; } #endif void *ael_yyalloc (yy_size_t size , yyscan_t yyscanner) { return (void *) malloc( size ); } void *ael_yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner) { /* The cast to (char *) in the following accommodates both * implementations that use char* generic pointers, and those * that use void* generic pointers. It works with the latter * because both ANSI C and C++ allow castless assignment from * any pointer type to void*, and deal with argument conversions * as though doing an assignment. */ return (void *) realloc( (char *) ptr, size ); } #define YYTABLES_NAME "yytables" #line 470 "ael.flex" static void pbcpush(char x) { pbcstack[pbcpos++] = x; } void ael_yyfree(void *ptr, yyscan_t yyscanner) { if (ptr) free( (char*) ptr ); } static int pbcpop(char x) { if ( ( x == ')' && pbcstack[pbcpos-1] == '(' ) || ( x == ']' && pbcstack[pbcpos-1] == '[' ) || ( x == '}' && pbcstack[pbcpos-1] == '{' )) { pbcpos--; return 0; } return 1; /* error */ } static int c_prevword(void) { char *c = prev_word; if (c == NULL) return 0; while ( *c ) { switch (*c) { case '{': case '[': case '(': pbcpush(*c); break; case '}': case ']': case ')': if (pbcpop(*c)) return 1; break; } c++; } return 0; } /* * The following three functions, reset_*, are used in the bison * code to switch context. As a consequence, we need to * declare them global and add a prototype so that the * compiler does not complain. * * NOTE: yyg is declared because it is used in the BEGIN macros, * though that should be hidden as the macro changes * depending on the flex options that we use - in particular, * %reentrant changes the way the macro is declared; * without %reentrant, BEGIN uses yystart instead of yyg */ void reset_parencount(yyscan_t yyscanner ); void reset_parencount(yyscan_t yyscanner ) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; parencount = 0; pbcpos = 0; pbcpush('('); /* push '(' so the last pcbpop (parencount= -1) will succeed */ c_prevword(); BEGIN(paren); } void reset_semicount(yyscan_t yyscanner ); void reset_semicount(yyscan_t yyscanner ) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; pbcpos = 0; BEGIN(semic); } void reset_argcount(yyscan_t yyscanner ); void reset_argcount(yyscan_t yyscanner ) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; parencount = 0; pbcpos = 0; pbcpush('('); /* push '(' so the last pcbpop (parencount= -1) will succeed */ c_prevword(); BEGIN(argg); } /* used elsewhere, but some local vars */ struct pval *ael2_parse(char *filename, int *errors) { struct pval *pval; struct parse_io *io; char *buffer; struct stat stats; FILE *fin; /* extern int ael_yydebug; */ io = calloc(sizeof(struct parse_io),1); /* reset the global counters */ prev_word = 0; my_lineno = 1; include_stack_index=0; my_col = 0; /* ael_yydebug = 1; */ ael_yylex_init(&io->scanner); fin = fopen(filename,"r"); if ( !fin ) { ast_log(LOG_ERROR,"File %s could not be opened\n", filename); *errors = 1; return 0; } if (my_file) free(my_file); my_file = strdup(filename); stat(filename, &stats); buffer = (char*)malloc(stats.st_size+2); fread(buffer, 1, stats.st_size, fin); buffer[stats.st_size]=0; fclose(fin); ael_yy_scan_string (buffer ,io->scanner); ael_yyset_lineno(1 , io->scanner); /* ael_yyset_in (fin , io->scanner); OLD WAY */ ael_yyparse(io); pval = io->pval; *errors = io->syntax_error_count; ael_yylex_destroy(io->scanner); free(buffer); free(io); return pval; } static void setup_filestack(char *fnamebuf2, int fnamebuf_siz, glob_t *globbuf, int globpos, yyscan_t yyscanner, int create) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; int error, i; FILE *in1; char fnamebuf[2048]; if (globbuf && globbuf->gl_pathv && globbuf->gl_pathc > 0) #if defined(STANDALONE) || defined(LOW_MEMORY) || defined(STANDALONE_AEL) strncpy(fnamebuf, globbuf->gl_pathv[globpos], fnamebuf_siz); #else ast_copy_string(fnamebuf, globbuf->gl_pathv[globpos], fnamebuf_siz); #endif else { ast_log(LOG_ERROR,"Include file name not present!\n"); return; } for (i=0; i * * 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 Bison Grammar description of AEL2. * */ #include "asterisk.h" ASTERISK_FILE_VERSION(__FILE__, "$Revision: 87168 $") #include #include #include #include "asterisk/logger.h" #include "asterisk/ael_structs.h" static pval * linku1(pval *head, pval *tail); static void set_dads(pval *dad, pval *child_list); void reset_parencount(yyscan_t yyscanner); void reset_semicount(yyscan_t yyscanner); void reset_argcount(yyscan_t yyscanner ); #define YYLEX_PARAM ((struct parse_io *)parseio)->scanner #define YYERROR_VERBOSE 1 extern char *my_file; #ifdef AAL_ARGCHECK int ael_is_funcname(char *name); #endif static char *ael_token_subst(const char *mess); %} %union { int intval; /* integer value, typically flags */ char *str; /* strings */ struct pval *pval; /* full objects */ } %{ /* declaring these AFTER the union makes things a lot simpler! */ void yyerror(YYLTYPE *locp, struct parse_io *parseio, char const *s); int ael_yylex (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , void * yyscanner); /* create a new object with start-end marker */ static pval *npval(pvaltype type, int first_line, int last_line, int first_column, int last_column); /* create a new object with start-end marker, simplified interface. * Must be declared here because YYLTYPE is not known before */ static pval *npval2(pvaltype type, YYLTYPE *first, YYLTYPE *last); /* another frontend for npval, this time for a string */ static pval *nword(char *string, YYLTYPE *pos); /* update end position of an object, return the object */ static pval *update_last(pval *, YYLTYPE *); %} %token KW_CONTEXT LC RC LP RP SEMI EQ COMMA COLON AMPER BAR AT %token KW_MACRO KW_GLOBALS KW_IGNOREPAT KW_SWITCH KW_IF KW_IFTIME KW_ELSE KW_RANDOM KW_ABSTRACT KW_EXTEND %token EXTENMARK KW_GOTO KW_JUMP KW_RETURN KW_BREAK KW_CONTINUE KW_REGEXTEN KW_HINT %token KW_FOR KW_WHILE KW_CASE KW_PATTERN KW_DEFAULT KW_CATCH KW_SWITCHES KW_ESWITCHES %token KW_INCLUDES %right BAR COMMA %token word %type includes %type includeslist %type switchlist %type eswitches %type switches %type macro_statement %type macro_statements %type case_statement %type case_statements %type eval_arglist %type application_call %type application_call_head %type macro_call %type target jumptarget %type statement %type switch_statement %type if_like_head %type statements %type extension %type ignorepat %type element %type elements %type arglist %type assignment %type global_statements %type globals %type macro %type context %type object %type objects %type file /* XXX lr changes */ %type opt_else %type timespec %type included_entry %type opt_word %type context_name %type timerange %type goto_word %type word_list %type word3_list hint_word %type test_expr %type opt_pri %type opt_abstract /* * OPTIONS */ %locations /* track source location using @n variables (yylloc in flex) */ %pure-parser /* pass yylval and yylloc as arguments to yylex(). */ %name-prefix="ael_yy" /* * add an additional argument, parseio, to yyparse(), * which is then accessible in the grammar actions */ %parse-param {struct parse_io *parseio} /* there will be two shift/reduce conflicts, they involve the if statement, where a single statement occurs not wrapped in curlies in the "true" section the default action to shift will attach the else to the preceeding if. */ %expect 3 %error-verbose /* * declare destructors for objects. * The former is for pval, the latter for strings. * NOTE: we must not have a destructor for a 'file' object. */ %destructor { destroy_pval($$); prev_word=0; } includes includeslist switchlist eswitches switches macro_statement macro_statements case_statement case_statements eval_arglist application_call application_call_head macro_call target jumptarget statement switch_statement if_like_head statements extension ignorepat element elements arglist assignment global_statements globals macro context object objects opt_else timespec included_entry %destructor { free($$);} word word_list goto_word word3_list opt_word context_name timerange test_expr opt_pri %% file : objects { $$ = parseio->pval = $1; } ; objects : object {$$=$1;} | objects object { $$ = linku1($1, $2); } | objects error {$$=$1;} ; object : context {$$=$1;} | macro {$$=$1;} | globals {$$=$1;} | SEMI {$$=0;/* allow older docs to be read */} ; context_name : word { $$ = $1; } | KW_DEFAULT { $$ = strdup("default"); } ; context : opt_abstract KW_CONTEXT context_name LC elements RC { $$ = npval2(PV_CONTEXT, &@1, &@6); $$->u1.str = $3; $$->u2.statements = $5; set_dads($$,$5); $$->u3.abstract = $1;} ; /* optional "abstract" keyword XXX there is no regression test for this */ opt_abstract: KW_ABSTRACT { $$ = 1; } | /* nothing */ { $$ = 0; } | KW_EXTEND { $$ = 2; } | KW_EXTEND KW_ABSTRACT { $$=3; } | KW_ABSTRACT KW_EXTEND { $$=3; } ; macro : KW_MACRO word LP arglist RP LC macro_statements RC { $$ = npval2(PV_MACRO, &@1, &@8); $$->u1.str = $2; $$->u2.arglist = $4; $$->u3.macro_statements = $7; set_dads($$,$7);} ; globals : KW_GLOBALS LC global_statements RC { $$ = npval2(PV_GLOBALS, &@1, &@4); $$->u1.statements = $3; set_dads($$,$3);} ; global_statements : { $$ = NULL; } | assignment global_statements {$$ = linku1($1, $2); } | error global_statements {$$=$2;} ; assignment : word EQ { reset_semicount(parseio->scanner); } word SEMI { $$ = npval2(PV_VARDEC, &@1, &@5); $$->u1.str = $1; $$->u2.val = $4; } ; /* XXX this matches missing arguments, is this desired ? */ arglist : /* empty */ { $$ = NULL; } | word { $$ = nword($1, &@1); } | arglist COMMA word { $$ = linku1($1, nword($3, &@3)); } | arglist error {$$=$1;} ; elements : {$$=0;} | element elements { $$ = linku1($1, $2); } | error elements { $$=$2;} ; element : extension {$$=$1;} | includes {$$=$1;} | switches {$$=$1;} | eswitches {$$=$1;} | ignorepat {$$=$1;} | assignment {$$=$1;} | word error {free($1); $$=0;} | SEMI {$$=0;/* allow older docs to be read */} ; ignorepat : KW_IGNOREPAT EXTENMARK word SEMI { $$ = npval2(PV_IGNOREPAT, &@1, &@4); $$->u1.str = $3;} ; extension : word EXTENMARK statement { $$ = npval2(PV_EXTENSION, &@1, &@3); $$->u1.str = $1; $$->u2.statements = $3; set_dads($$,$3);} | KW_REGEXTEN word EXTENMARK statement { $$ = npval2(PV_EXTENSION, &@1, &@4); $$->u1.str = $2; $$->u2.statements = $4; set_dads($$,$4); $$->u4.regexten=1;} | KW_HINT LP hint_word RP word EXTENMARK statement { $$ = npval2(PV_EXTENSION, &@1, &@7); $$->u1.str = $5; $$->u2.statements = $7; set_dads($$,$7); $$->u3.hints = $3;} | KW_REGEXTEN KW_HINT LP hint_word RP word EXTENMARK statement { $$ = npval2(PV_EXTENSION, &@1, &@8); $$->u1.str = $6; $$->u2.statements = $8; set_dads($$,$8); $$->u4.regexten=1; $$->u3.hints = $4;} ; /* list of statements in a block or after a case label - can be empty */ statements : /* empty */ { $$ = NULL; } | statement statements { $$ = linku1($1, $2); } | error statements {$$=$2;} ; /* hh:mm-hh:mm, due to the way the parser works we do not * detect the '-' but only the ':' as separator */ timerange: word3_list COLON word3_list COLON word3_list { asprintf(&$$, "%s:%s:%s", $1, $3, $5); free($1); free($3); free($5); } | word { $$ = $1; } ; /* full time specification range|dow|*|* */ timespec : timerange BAR word3_list BAR word3_list BAR word3_list { $$ = nword($1, &@1); $$->next = nword($3, &@3); $$->next->next = nword($5, &@5); $$->next->next->next = nword($7, &@7); } ; /* expression used in if, random, while, switch */ test_expr : LP { reset_parencount(parseio->scanner); } word_list RP { $$ = $3; } ; /* 'if' like statements: if, iftime, random */ if_like_head : KW_IF test_expr { $$= npval2(PV_IF, &@1, &@2); $$->u1.str = $2; } | KW_RANDOM test_expr { $$ = npval2(PV_RANDOM, &@1, &@2); $$->u1.str=$2;} | KW_IFTIME LP timespec RP { $$ = npval2(PV_IFTIME, &@1, &@4); $$->u1.list = $3; prev_word = 0; } ; /* word_list is a hack to fix a problem with context switching between bison and flex; by the time you register a new context with flex, you've already got a look-ahead token from the old context, with no way to put it back and start afresh. So, we kludge this and merge the words back together. */ word_list : word { $$ = $1;} | word word { asprintf(&($$), "%s%s", $1, $2); free($1); free($2); prev_word = $$;} ; hint_word : word { $$ = $1; } | hint_word word { asprintf(&($$), "%s %s", $1, $2); free($1); free($2); } | hint_word COLON word { asprintf(&($$), "%s:%s", $1, $3); free($1); free($3); } | hint_word AMPER word { /* there are often '&' in hints */ asprintf(&($$), "%s&%s", $1, $3); free($1); free($3);} word3_list : word { $$ = $1;} | word word { asprintf(&($$), "%s%s", $1, $2); free($1); free($2); prev_word = $$;} | word word word { asprintf(&($$), "%s%s%s", $1, $2, $3); free($1); free($2); free($3); prev_word=$$;} ; goto_word : word { $$ = $1;} | word word { asprintf(&($$), "%s%s", $1, $2); free($1); free($2);} | goto_word COLON word { asprintf(&($$), "%s:%s", $1, $3); free($1); free($3);} ; switch_statement : KW_SWITCH test_expr LC case_statements RC { $$ = npval2(PV_SWITCH, &@1, &@5); $$->u1.str = $2; $$->u2.statements = $4; set_dads($$,$4);} ; /* * Definition of a statememt in our language */ statement : LC statements RC { $$ = npval2(PV_STATEMENTBLOCK, &@1, &@3); $$->u1.list = $2; set_dads($$,$2);} | assignment { $$ = $1; } | KW_GOTO target SEMI { $$ = npval2(PV_GOTO, &@1, &@3); $$->u1.list = $2;} | KW_JUMP jumptarget SEMI { $$ = npval2(PV_GOTO, &@1, &@3); $$->u1.list = $2;} | word COLON { $$ = npval2(PV_LABEL, &@1, &@2); $$->u1.str = $1; } | KW_FOR LP {reset_semicount(parseio->scanner);} word SEMI {reset_semicount(parseio->scanner);} word SEMI {reset_parencount(parseio->scanner);} word RP statement { /* XXX word_list maybe ? */ $$ = npval2(PV_FOR, &@1, &@12); $$->u1.for_init = $4; $$->u2.for_test=$7; $$->u3.for_inc = $10; $$->u4.for_statements = $12; set_dads($$,$12);} | KW_WHILE test_expr statement { $$ = npval2(PV_WHILE, &@1, &@3); $$->u1.str = $2; $$->u2.statements = $3; set_dads($$,$3);} | switch_statement { $$ = $1; } | AMPER macro_call SEMI { $$ = update_last($2, &@2); } | application_call SEMI { $$ = update_last($1, &@2); } | word SEMI { $$= npval2(PV_APPLICATION_CALL, &@1, &@2); $$->u1.str = $1;} | application_call EQ {reset_semicount(parseio->scanner);} word SEMI { char *bufx; int tot=0; pval *pptr; $$ = npval2(PV_VARDEC, &@1, &@5); $$->u2.val=$4; /* rebuild the original string-- this is not an app call, it's an unwrapped vardec, with a func call on the LHS */ /* string to big to fit in the buffer? */ tot+=strlen($1->u1.str); for(pptr=$1->u2.arglist;pptr;pptr=pptr->next) { tot+=strlen(pptr->u1.str); tot++; /* for a sep like a comma */ } tot+=4; /* for safety */ bufx = calloc(1, tot); strcpy(bufx,$1->u1.str); strcat(bufx,"("); /* XXX need to advance the pointer or the loop is very inefficient */ for (pptr=$1->u2.arglist;pptr;pptr=pptr->next) { if ( pptr != $1->u2.arglist ) strcat(bufx,","); strcat(bufx,pptr->u1.str); } strcat(bufx,")"); #ifdef AAL_ARGCHECK if ( !ael_is_funcname($1->u1.str) ) ast_log(LOG_WARNING, "==== File: %s, Line %d, Cols: %d-%d: Function call? The name %s is not in my internal list of function names\n", my_file, @1.first_line, @1.first_column, @1.last_column, $1->u1.str); #endif $$->u1.str = bufx; destroy_pval($1); /* the app call it is not, get rid of that chain */ prev_word = 0; } | KW_BREAK SEMI { $$ = npval2(PV_BREAK, &@1, &@2); } | KW_RETURN SEMI { $$ = npval2(PV_RETURN, &@1, &@2); } | KW_CONTINUE SEMI { $$ = npval2(PV_CONTINUE, &@1, &@2); } | if_like_head statement opt_else { $$ = update_last($1, &@2); $$->u2.statements = $2; set_dads($$,$2); $$->u3.else_statements = $3;set_dads($$,$3);} | SEMI { $$=0; } ; opt_else : KW_ELSE statement { $$ = $2; } | { $$ = NULL ; } target : goto_word { $$ = nword($1, &@1); } | goto_word BAR goto_word { $$ = nword($1, &@1); $$->next = nword($3, &@3); } | goto_word COMMA goto_word { $$ = nword($1, &@1); $$->next = nword($3, &@3); } | goto_word BAR goto_word BAR goto_word { $$ = nword($1, &@1); $$->next = nword($3, &@3); $$->next->next = nword($5, &@5); } | goto_word COMMA goto_word COMMA goto_word { $$ = nword($1, &@1); $$->next = nword($3, &@3); $$->next->next = nword($5, &@5); } | KW_DEFAULT BAR goto_word BAR goto_word { $$ = nword(strdup("default"), &@1); $$->next = nword($3, &@3); $$->next->next = nword($5, &@5); } | KW_DEFAULT COMMA goto_word COMMA goto_word { $$ = nword(strdup("default"), &@1); $$->next = nword($3, &@3); $$->next->next = nword($5, &@5); } ; opt_pri : /* empty */ { $$ = strdup("1"); } | COMMA word { $$ = $2; } ; /* XXX please document the form of jumptarget */ jumptarget : goto_word opt_pri { /* ext[, pri] default 1 */ $$ = nword($1, &@1); $$->next = nword($2, &@2); } /* jump extension[,priority][@context] */ | goto_word opt_pri AT context_name { /* context, ext, pri */ $$ = nword($4, &@4); $$->next = nword($1, &@1); $$->next->next = nword($2, &@2); } ; macro_call : word LP {reset_argcount(parseio->scanner);} eval_arglist RP { /* XXX original code had @2 but i think we need @5 */ $$ = npval2(PV_MACRO_CALL, &@1, &@5); $$->u1.str = $1; $$->u2.arglist = $4;} | word LP RP { $$= npval2(PV_MACRO_CALL, &@1, &@3); $$->u1.str = $1; } ; /* XXX application_call_head must be revised. Having 'word LP { ...' * just as above should work fine, however it gives a different result. */ application_call_head: word LP {reset_argcount(parseio->scanner);} { if (strcasecmp($1,"goto") == 0) { $$ = npval2(PV_GOTO, &@1, &@2); free($1); /* won't be using this */ ast_log(LOG_WARNING, "==== File: %s, Line %d, Cols: %d-%d: Suggestion: Use the goto statement instead of the Goto() application call in AEL.\n", my_file, @1.first_line, @1.first_column, @1.last_column ); } else { $$= npval2(PV_APPLICATION_CALL, &@1, &@2); $$->u1.str = $1; } } ; application_call : application_call_head eval_arglist RP { $$ = update_last($1, &@3); if( $$->type == PV_GOTO ) $$->u1.list = $2; else $$->u2.arglist = $2; } | application_call_head RP { $$ = update_last($1, &@2); } ; opt_word : word { $$ = $1 } | { $$ = strdup(""); } ; eval_arglist : word_list { $$ = nword($1, &@1); } | /*nothing! */ { $$= npval(PV_WORD,0/*@1.first_line*/,0/*@1.last_line*/,0/* @1.first_column*/, 0/*@1.last_column*/); $$->u1.str = strdup(""); } | eval_arglist COMMA opt_word { $$ = linku1($1, nword($3, &@3)); } ; case_statements: /* empty */ { $$ = NULL; } | case_statement case_statements { $$ = linku1($1, $2); } ; case_statement: KW_CASE word COLON statements { $$ = npval2(PV_CASE, &@1, &@3); /* XXX 3 or 4 ? */ $$->u1.str = $2; $$->u2.statements = $4; set_dads($$,$4);} | KW_DEFAULT COLON statements { $$ = npval2(PV_DEFAULT, &@1, &@3); $$->u1.str = NULL; $$->u2.statements = $3;set_dads($$,$3);} | KW_PATTERN word COLON statements { $$ = npval2(PV_PATTERN, &@1, &@4); /* XXX@3 or @4 ? */ $$->u1.str = $2; $$->u2.statements = $4;set_dads($$,$4);} ; macro_statements: /* empty */ { $$ = NULL; } | macro_statement macro_statements { $$ = linku1($1, $2); } ; macro_statement : statement {$$=$1;} | includes { $$=$1;} | KW_CATCH word LC statements RC { $$ = npval2(PV_CATCH, &@1, &@5); $$->u1.str = $2; $$->u2.statements = $4; set_dads($$,$4);} ; switches : KW_SWITCHES LC switchlist RC { $$ = npval2(PV_SWITCHES, &@1, &@2); $$->u1.list = $3; set_dads($$,$3);} ; eswitches : KW_ESWITCHES LC switchlist RC { $$ = npval2(PV_ESWITCHES, &@1, &@2); $$->u1.list = $3; set_dads($$,$3);} ; switchlist : /* empty */ { $$ = NULL; } | word SEMI switchlist { $$ = linku1(nword($1, &@1), $3); } | word AT word SEMI switchlist { char *x; asprintf(&x,"%s@%s", $1,$3); free($1); free($3); $$ = linku1(nword(x, &@1), $5);} | error switchlist {$$=$2;} ; included_entry : context_name { $$ = nword($1, &@1); } | context_name BAR timespec { $$ = nword($1, &@1); $$->u2.arglist = $3; prev_word=0; /* XXX sure ? */ } ; /* list of ';' separated context names followed by optional timespec */ includeslist : included_entry SEMI { $$ = $1; } | includeslist included_entry SEMI { $$ = linku1($1, $2); } | includeslist error {$$=$1;} ; includes : KW_INCLUDES LC includeslist RC { $$ = npval2(PV_INCLUDES, &@1, &@4); $$->u1.list = $3;set_dads($$,$3);} | KW_INCLUDES LC RC { $$ = npval2(PV_INCLUDES, &@1, &@3);} ; %% static char *token_equivs1[] = { "AMPER", "AT", "BAR", "COLON", "COMMA", "EQ", "EXTENMARK", "KW_BREAK", "KW_CASE", "KW_CATCH", "KW_CONTEXT", "KW_CONTINUE", "KW_DEFAULT", "KW_ELSE", "KW_ESWITCHES", "KW_FOR", "KW_GLOBALS", "KW_GOTO", "KW_HINT", "KW_IFTIME", "KW_IF", "KW_IGNOREPAT", "KW_INCLUDES" "KW_JUMP", "KW_MACRO", "KW_PATTERN", "KW_REGEXTEN", "KW_RETURN", "KW_SWITCHES", "KW_SWITCH", "KW_WHILE", "LC", "LP", "RC", "RP", "SEMI", }; static char *token_equivs2[] = { "&", "@", "|", ":", ",", "=", "=>", "break", "case", "catch", "context", "continue", "default", "else", "eswitches", "for", "globals", "goto", "hint", "ifTime", "if", "ignorepat", "includes" "jump", "macro", "pattern", "regexten", "return", "switches", "switch", "while", "{", "(", "}", ")", ";", }; static char *ael_token_subst(const char *mess) { /* calc a length, malloc, fill, and return; yyerror had better free it! */ int len=0,i; const char *p; char *res, *s,*t; int token_equivs_entries = sizeof(token_equivs1)/sizeof(char*); for (p=mess; *p; p++) { for (i=0; ifirst_line == locp->last_line) { ast_log(LOG_ERROR, "==== File: %s, Line %d, Cols: %d-%d: Error: %s\n", my_file, locp->first_line, locp->first_column, locp->last_column, s2); } else { ast_log(LOG_ERROR, "==== File: %s, Line %d Col %d to Line %d Col %d: Error: %s\n", my_file, locp->first_line, locp->first_column, locp->last_line, locp->last_column, s2); } free(s2); parseio->syntax_error_count++; } static struct pval *npval(pvaltype type, int first_line, int last_line, int first_column, int last_column) { pval *z = calloc(1, sizeof(struct pval)); z->type = type; z->startline = first_line; z->endline = last_line; z->startcol = first_column; z->endcol = last_column; z->filename = strdup(my_file); return z; } static struct pval *npval2(pvaltype type, YYLTYPE *first, YYLTYPE *last) { return npval(type, first->first_line, last->last_line, first->first_column, last->last_column); } static struct pval *update_last(pval *obj, YYLTYPE *last) { obj->endline = last->last_line; obj->endcol = last->last_column; return obj; } /* frontend for npval to create a PV_WORD string from the given token */ static pval *nword(char *string, YYLTYPE *pos) { pval *p = npval2(PV_WORD, pos, pos); if (p) p->u1.str = string; return p; } /* append second element to the list in the first one */ static pval * linku1(pval *head, pval *tail) { if (!head) return tail; if (tail) { if (!head->next) { head->next = tail; } else { head->u1_last->next = tail; } head->u1_last = tail; tail->prev = head; /* the dad link only points to containers */ } return head; } /* this routine adds a dad ptr to each element in the list */ static void set_dads(struct pval *dad, struct pval *child_list) { struct pval *t; for(t=child_list;t;t=t->next) /* simple stuff */ t->dad = dad; } asterisk-1.4.21.2/pbx/ael/ael.tab.h0000644000175000017500000000715710710413372016047 0ustar maniacmaniac/* A Bison parser, made by GNU Bison 2.1a. */ /* Skeleton parser for Yacc-like parsing with Bison, Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* As a special exception, when this file is copied by Bison into a Bison output file, you may use that output file without restriction. This special exception was added by the Free Software Foundation in version 1.24 of Bison. */ /* Tokens. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE /* Put the tokens into the symbol table, so that GDB and other debuggers know about them. */ enum yytokentype { KW_CONTEXT = 258, LC = 259, RC = 260, LP = 261, RP = 262, SEMI = 263, EQ = 264, COMMA = 265, COLON = 266, AMPER = 267, BAR = 268, AT = 269, KW_MACRO = 270, KW_GLOBALS = 271, KW_IGNOREPAT = 272, KW_SWITCH = 273, KW_IF = 274, KW_IFTIME = 275, KW_ELSE = 276, KW_RANDOM = 277, KW_ABSTRACT = 278, KW_EXTEND = 279, EXTENMARK = 280, KW_GOTO = 281, KW_JUMP = 282, KW_RETURN = 283, KW_BREAK = 284, KW_CONTINUE = 285, KW_REGEXTEN = 286, KW_HINT = 287, KW_FOR = 288, KW_WHILE = 289, KW_CASE = 290, KW_PATTERN = 291, KW_DEFAULT = 292, KW_CATCH = 293, KW_SWITCHES = 294, KW_ESWITCHES = 295, KW_INCLUDES = 296, word = 297 }; #endif /* Tokens. */ #define KW_CONTEXT 258 #define LC 259 #define RC 260 #define LP 261 #define RP 262 #define SEMI 263 #define EQ 264 #define COMMA 265 #define COLON 266 #define AMPER 267 #define BAR 268 #define AT 269 #define KW_MACRO 270 #define KW_GLOBALS 271 #define KW_IGNOREPAT 272 #define KW_SWITCH 273 #define KW_IF 274 #define KW_IFTIME 275 #define KW_ELSE 276 #define KW_RANDOM 277 #define KW_ABSTRACT 278 #define KW_EXTEND 279 #define EXTENMARK 280 #define KW_GOTO 281 #define KW_JUMP 282 #define KW_RETURN 283 #define KW_BREAK 284 #define KW_CONTINUE 285 #define KW_REGEXTEN 286 #define KW_HINT 287 #define KW_FOR 288 #define KW_WHILE 289 #define KW_CASE 290 #define KW_PATTERN 291 #define KW_DEFAULT 292 #define KW_CATCH 293 #define KW_SWITCHES 294 #define KW_ESWITCHES 295 #define KW_INCLUDES 296 #define word 297 #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED typedef union YYSTYPE #line 54 "ael.y" { int intval; /* integer value, typically flags */ char *str; /* strings */ struct pval *pval; /* full objects */ } /* Line 1536 of yacc.c. */ #line 129 "ael.tab.h" YYSTYPE; # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 # define YYSTYPE_IS_TRIVIAL 1 #endif #if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED typedef struct YYLTYPE { int first_line; int first_column; int last_line; int last_column; } YYLTYPE; # define yyltype YYLTYPE /* obsolescent; will be withdrawn */ # define YYLTYPE_IS_DECLARED 1 # define YYLTYPE_IS_TRIVIAL 1 #endif asterisk-1.4.21.2/pbx/ael/ael.flex0000644000175000017500000004470410767661433016027 0ustar maniacmaniac/* * Asterisk -- An open source telephony toolkit. * * Copyright (C) 2006, Digium, Inc. * * Steve Murphy * * 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 Flex scanner description of tokens used in AEL2 . * */ /* * Start with flex options: * * %x describes the contexts we have: paren, semic and argg, plus INITIAL */ %x paren semic argg comment /* prefix used for various globally-visible functions and variables. * This renames also yywrap, but since we do not use it, we just * add option noyywrap to remove it. */ %option prefix="ael_yy" %option noyywrap /* yyfree normally just frees its arg. It can be null sometimes, which some systems will complain about, so, we'll define our own version */ %option noyyfree /* batch gives a bit more performance if we are using it in * a non-interactive mode. We probably don't care much. */ %option batch /* outfile is the filename to be used instead of lex.yy.c */ %option outfile="ael_lex.c" /* * These are not supported in flex 2.5.4, but we need them * at the moment: * reentrant produces a thread-safe parser. Not 100% sure that * we require it, though. * bison-bridge passes an additional yylval argument to yylex(). * bison-locations is probably not needed. */ %option reentrant %option bison-bridge %option bison-locations %{ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 109309 $") #include #include #include #if defined(__Darwin__) || defined(__CYGWIN__) #define GLOB_ABORTED GLOB_ABEND #endif # include #include "asterisk/logger.h" #include "asterisk/utils.h" #include "ael/ael.tab.h" #include "asterisk/ael_structs.h" /* * A stack to keep track of matching brackets ( [ { } ] ) */ static char pbcstack[400]; /* XXX missing size checks */ static int pbcpos = 0; static void pbcpush(char x); static int pbcpop(char x); static int parencount = 0; /* * current line, column and filename, updated as we read the input. */ static int my_lineno = 1; /* current line in the source */ static int my_col = 1; /* current column in the source */ char *my_file = 0; /* used also in the bison code */ char *prev_word; /* XXX document it */ #define MAX_INCLUDE_DEPTH 50 /* * flex is not too smart, and generates global functions * without prototypes so the compiler may complain. * To avoid that, we declare the prototypes here, * even though these functions are not used. */ int ael_yyget_column (yyscan_t yyscanner); void ael_yyset_column (int column_no , yyscan_t yyscanner); int ael_yyparse (struct parse_io *); /* * A stack to process include files. * As we switch into the new file we need to store the previous * state to restore it later. */ struct stackelement { char *fname; int lineno; int colno; glob_t globbuf; /* the current globbuf */ int globbuf_pos; /* where we are in the current globbuf */ YY_BUFFER_STATE bufstate; }; static struct stackelement include_stack[MAX_INCLUDE_DEPTH]; static int include_stack_index = 0; static void setup_filestack(char *fnamebuf, int fnamebuf_siz, glob_t *globbuf, int globpos, yyscan_t xscan, int create); /* * if we use the @n feature of bison, we must supply the start/end * location of tokens in the structure pointed by yylloc. * Simple tokens are just assumed to be on the same line, so * the line number is constant, and the column is incremented * by the length of the token. */ #ifdef FLEX_BETA /* set for 2.5.33 */ /* compute the total number of lines and columns in the text * passed as argument. */ static void pbcwhere(const char *text, int *line, int *col ) { int loc_line = *line; int loc_col = *col; char c; while ( (c = *text++) ) { if ( c == '\t' ) { loc_col += 8 - (loc_col % 8); } else if ( c == '\n' ) { loc_line++; loc_col = 1; } else loc_col++; } *line = loc_line; *col = loc_col; } #define STORE_POS do { \ yylloc->first_line = yylloc->last_line = my_lineno; \ yylloc->first_column=my_col; \ yylloc->last_column=my_col+yyleng-1; \ my_col+=yyleng; \ } while (0) #define STORE_LOC do { \ yylloc->first_line = my_lineno; \ yylloc->first_column=my_col; \ pbcwhere(yytext, &my_lineno, &my_col); \ yylloc->last_line = my_lineno; \ yylloc->last_column = my_col - 1; \ } while (0) #else #define STORE_POS #define STORE_LOC #endif %} NOPARENS ([^()\[\]\{\}]|\\[()\[\]\{\}])* NOARGG ([^(),\{\}\[\]]|\\[,()\[\]\{\}])* NOSEMIC ([^;()\{\}\[\]]|\\[;()\[\]\{\}])* %% \{ { STORE_POS; return LC;} \} { STORE_POS; return RC;} \( { STORE_POS; return LP;} \) { STORE_POS; return RP;} \; { STORE_POS; return SEMI;} \= { STORE_POS; return EQ;} \, { STORE_POS; return COMMA;} \: { STORE_POS; return COLON;} \& { STORE_POS; return AMPER;} \| { STORE_POS; return BAR;} \=\> { STORE_POS; return EXTENMARK;} \@ { STORE_POS; return AT;} \/\/[^\n]* {/*comment*/} context { STORE_POS; return KW_CONTEXT;} abstract { STORE_POS; return KW_ABSTRACT;} extend { STORE_POS; return KW_EXTEND;} macro { STORE_POS; return KW_MACRO;}; globals { STORE_POS; return KW_GLOBALS;} ignorepat { STORE_POS; return KW_IGNOREPAT;} switch { STORE_POS; return KW_SWITCH;} if { STORE_POS; return KW_IF;} ifTime { STORE_POS; return KW_IFTIME;} random { STORE_POS; return KW_RANDOM;} regexten { STORE_POS; return KW_REGEXTEN;} hint { STORE_POS; return KW_HINT;} else { STORE_POS; return KW_ELSE;} goto { STORE_POS; return KW_GOTO;} jump { STORE_POS; return KW_JUMP;} return { STORE_POS; return KW_RETURN;} break { STORE_POS; return KW_BREAK;} continue { STORE_POS; return KW_CONTINUE;} for { STORE_POS; return KW_FOR;} while { STORE_POS; return KW_WHILE;} case { STORE_POS; return KW_CASE;} default { STORE_POS; return KW_DEFAULT;} pattern { STORE_POS; return KW_PATTERN;} catch { STORE_POS; return KW_CATCH;} switches { STORE_POS; return KW_SWITCHES;} eswitches { STORE_POS; return KW_ESWITCHES;} includes { STORE_POS; return KW_INCLUDES;} "/*" { BEGIN(comment); my_col += 2; } [^*\n]* { my_col += yyleng; } [^*\n]*\n { ++my_lineno; my_col=1;} "*"+[^*/\n]* { my_col += yyleng; } "*"+[^*/\n]*\n { ++my_lineno; my_col=1;} "*/" { my_col += 2; BEGIN(INITIAL); } \n { my_lineno++; my_col = 1; } [ ]+ { my_col += yyleng; } [\t]+ { my_col += (yyleng*8)-(my_col%8); } [-a-zA-Z0-9'"_/.\<\>\*\+!$#\[\]][-a-zA-Z0-9'"_/.!\*\+\<\>\{\}$#\[\]]* { STORE_POS; yylval->str = strdup(yytext); prev_word = yylval->str; return word; } /* * context used for arguments of if_head, random_head, switch_head, * for (last statement), while (XXX why not iftime_head ?). * End with the matching parentheses. * A comma at the top level is valid here, unlike in argg where it * is an argument separator so it must be returned as a token. */ {NOPARENS}\) { if ( pbcpop(')') ) { /* error */ STORE_LOC; ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Mismatched ')' in expression: %s !\n", my_file, my_lineno, my_col, yytext); BEGIN(0); yylval->str = strdup(yytext); prev_word = 0; return word; } parencount--; if ( parencount >= 0) { yymore(); } else { STORE_LOC; yylval->str = strdup(yytext); yylval->str[yyleng-1] = '\0'; /* trim trailing ')' */ unput(')'); BEGIN(0); return word; } } {NOPARENS}[\(\[\{] { char c = yytext[yyleng-1]; if (c == '(') parencount++; pbcpush(c); yymore(); } {NOPARENS}[\]\}] { char c = yytext[yyleng-1]; if ( pbcpop(c)) { /* error */ STORE_LOC; ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Mismatched '%c' in expression!\n", my_file, my_lineno, my_col, c); BEGIN(0); yylval->str = strdup(yytext); return word; } yymore(); } /* * handlers for arguments to a macro or application calls. * We enter this context when we find the initial '(' and * stay here until we close all matching parentheses, * and find the comma (argument separator) or the closing ')' * of the (external) call, which happens when parencount == 0 * before the decrement. */ {NOARGG}[\(\[\{] { char c = yytext[yyleng-1]; if (c == '(') parencount++; pbcpush(c); yymore(); } {NOARGG}\) { if ( pbcpop(')') ) { /* error */ STORE_LOC; ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Mismatched ')' in expression!\n", my_file, my_lineno, my_col); BEGIN(0); yylval->str = strdup(yytext); return word; } parencount--; if( parencount >= 0){ yymore(); } else { STORE_LOC; BEGIN(0); if ( !strcmp(yytext, ")") ) return RP; yylval->str = strdup(yytext); yylval->str[yyleng-1] = '\0'; /* trim trailing ')' */ unput(')'); return word; } } {NOARGG}\, { if( parencount != 0) { /* printf("Folding in a comma!\n"); */ yymore(); } else { STORE_LOC; if( !strcmp(yytext,"," ) ) return COMMA; yylval->str = strdup(yytext); yylval->str[yyleng-1] = '\0'; unput(','); return word; } } {NOARGG}[\]\}] { char c = yytext[yyleng-1]; if ( pbcpop(c) ) { /* error */ STORE_LOC; ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Mismatched '%c' in expression!\n", my_file, my_lineno, my_col, c); BEGIN(0); yylval->str = strdup(yytext); return word; } yymore(); } /* * context used to find tokens in the right hand side of assignments, * or in the first and second operand of a 'for'. As above, match * commas and use ';' as a separator (hence return it as a separate token). */ {NOSEMIC}[\(\[\{] { char c = yytext[yyleng-1]; yymore(); pbcpush(c); } {NOSEMIC}[\)\]\}] { char c = yytext[yyleng-1]; if ( pbcpop(c) ) { /* error */ STORE_LOC; ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Mismatched '%c' in expression!\n", my_file, my_lineno, my_col, c); BEGIN(0); yylval->str = strdup(yytext); return word; } yymore(); } {NOSEMIC}; { STORE_LOC; yylval->str = strdup(yytext); yylval->str[yyleng-1] = '\0'; unput(';'); BEGIN(0); return word; } \#include[ \t]+\"[^\"]+\" { char fnamebuf[1024],*p1,*p2; int glob_ret; glob_t globbuf; /* the current globbuf */ int globbuf_pos = -1; /* where we are in the current globbuf */ globbuf.gl_offs = 0; /* initialize it to silence gcc */ p1 = strchr(yytext,'"'); p2 = strrchr(yytext,'"'); if ( include_stack_index >= MAX_INCLUDE_DEPTH ) { ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Includes nested too deeply! Wow!!! How did you do that?\n", my_file, my_lineno, my_col); } else if ( (int)(p2-p1) > sizeof(fnamebuf) - 1 ) { ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Filename is incredibly way too long (%d chars!). Inclusion ignored!\n", my_file, my_lineno, my_col, yyleng - 10); } else { strncpy(fnamebuf, p1+1, p2-p1-1); fnamebuf[p2-p1-1] = 0; #ifdef SOLARIS glob_ret = glob(fnamebuf, GLOB_NOCHECK, NULL, &globbuf); #else glob_ret = glob(fnamebuf, 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", fnamebuf); } else if (glob_ret == GLOB_ABORTED) { ast_log(LOG_WARNING, "Glob Expansion of pattern '%s' failed: Read error\n", fnamebuf); } else if (glob_ret == GLOB_NOMATCH) { ast_log(LOG_WARNING, "Glob Expansion of pattern '%s' failed: No matches!\n", fnamebuf); } else { globbuf_pos = 0; } } if (globbuf_pos > -1) { setup_filestack(fnamebuf, sizeof(fnamebuf), &globbuf, 0, yyscanner, 1); } } <> { char fnamebuf[2048]; if (include_stack_index > 0 && include_stack[include_stack_index-1].globbuf_pos < include_stack[include_stack_index-1].globbuf.gl_pathc-1) { yy_delete_buffer( YY_CURRENT_BUFFER, yyscanner ); include_stack[include_stack_index-1].globbuf_pos++; setup_filestack(fnamebuf, sizeof(fnamebuf), &include_stack[include_stack_index-1].globbuf, include_stack[include_stack_index-1].globbuf_pos, yyscanner, 0); /* finish this */ } else { if (include_stack[include_stack_index].fname) { free(include_stack[include_stack_index].fname); include_stack[include_stack_index].fname = 0; } if (my_file) { free(my_file); my_file = 0; } if ( --include_stack_index < 0 ) { yyterminate(); } else { globfree(&include_stack[include_stack_index].globbuf); include_stack[include_stack_index].globbuf_pos = -1; yy_delete_buffer( YY_CURRENT_BUFFER, yyscanner ); yy_switch_to_buffer(include_stack[include_stack_index].bufstate, yyscanner ); my_lineno = include_stack[include_stack_index].lineno; my_col = include_stack[include_stack_index].colno; my_file = strdup(include_stack[include_stack_index].fname); } } } %% static void pbcpush(char x) { pbcstack[pbcpos++] = x; } void ael_yyfree(void *ptr, yyscan_t yyscanner) { if (ptr) free( (char*) ptr ); } static int pbcpop(char x) { if ( ( x == ')' && pbcstack[pbcpos-1] == '(' ) || ( x == ']' && pbcstack[pbcpos-1] == '[' ) || ( x == '}' && pbcstack[pbcpos-1] == '{' )) { pbcpos--; return 0; } return 1; /* error */ } static int c_prevword(void) { char *c = prev_word; if (c == NULL) return 0; while ( *c ) { switch (*c) { case '{': case '[': case '(': pbcpush(*c); break; case '}': case ']': case ')': if (pbcpop(*c)) return 1; break; } c++; } return 0; } /* * The following three functions, reset_*, are used in the bison * code to switch context. As a consequence, we need to * declare them global and add a prototype so that the * compiler does not complain. * * NOTE: yyg is declared because it is used in the BEGIN macros, * though that should be hidden as the macro changes * depending on the flex options that we use - in particular, * %reentrant changes the way the macro is declared; * without %reentrant, BEGIN uses yystart instead of yyg */ void reset_parencount(yyscan_t yyscanner ); void reset_parencount(yyscan_t yyscanner ) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; parencount = 0; pbcpos = 0; pbcpush('('); /* push '(' so the last pcbpop (parencount= -1) will succeed */ c_prevword(); BEGIN(paren); } void reset_semicount(yyscan_t yyscanner ); void reset_semicount(yyscan_t yyscanner ) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; pbcpos = 0; BEGIN(semic); } void reset_argcount(yyscan_t yyscanner ); void reset_argcount(yyscan_t yyscanner ) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; parencount = 0; pbcpos = 0; pbcpush('('); /* push '(' so the last pcbpop (parencount= -1) will succeed */ c_prevword(); BEGIN(argg); } /* used elsewhere, but some local vars */ struct pval *ael2_parse(char *filename, int *errors) { struct pval *pval; struct parse_io *io; char *buffer; struct stat stats; FILE *fin; /* extern int ael_yydebug; */ io = calloc(sizeof(struct parse_io),1); /* reset the global counters */ prev_word = 0; my_lineno = 1; include_stack_index=0; my_col = 0; /* ael_yydebug = 1; */ ael_yylex_init(&io->scanner); fin = fopen(filename,"r"); if ( !fin ) { ast_log(LOG_ERROR,"File %s could not be opened\n", filename); *errors = 1; return 0; } if (my_file) free(my_file); my_file = strdup(filename); stat(filename, &stats); buffer = (char*)malloc(stats.st_size+2); fread(buffer, 1, stats.st_size, fin); buffer[stats.st_size]=0; fclose(fin); ael_yy_scan_string (buffer ,io->scanner); ael_yyset_lineno(1 , io->scanner); /* ael_yyset_in (fin , io->scanner); OLD WAY */ ael_yyparse(io); pval = io->pval; *errors = io->syntax_error_count; ael_yylex_destroy(io->scanner); free(buffer); free(io); return pval; } static void setup_filestack(char *fnamebuf2, int fnamebuf_siz, glob_t *globbuf, int globpos, yyscan_t yyscanner, int create) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; int error, i; FILE *in1; char fnamebuf[2048]; if (globbuf && globbuf->gl_pathv && globbuf->gl_pathc > 0) #if defined(STANDALONE) || defined(LOW_MEMORY) || defined(STANDALONE_AEL) strncpy(fnamebuf, globbuf->gl_pathv[globpos], fnamebuf_siz); #else ast_copy_string(fnamebuf, globbuf->gl_pathv[globpos], fnamebuf_siz); #endif else { ast_log(LOG_ERROR,"Include file name not present!\n"); return; } for (i=0; i * * 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 Bison Grammar description of AEL2. * */ #include "asterisk.h" ASTERISK_FILE_VERSION(__FILE__, "$Revision: 87168 $") #include #include #include #include "asterisk/logger.h" #include "asterisk/ael_structs.h" static pval * linku1(pval *head, pval *tail); static void set_dads(pval *dad, pval *child_list); void reset_parencount(yyscan_t yyscanner); void reset_semicount(yyscan_t yyscanner); void reset_argcount(yyscan_t yyscanner ); #define YYLEX_PARAM ((struct parse_io *)parseio)->scanner #define YYERROR_VERBOSE 1 extern char *my_file; #ifdef AAL_ARGCHECK int ael_is_funcname(char *name); #endif static char *ael_token_subst(const char *mess); /* Enabling traces. */ #ifndef YYDEBUG # define YYDEBUG 0 #endif /* Enabling verbose error messages. */ #ifdef YYERROR_VERBOSE # undef YYERROR_VERBOSE # define YYERROR_VERBOSE 1 #else # define YYERROR_VERBOSE 1 #endif /* Enabling the token table. */ #ifndef YYTOKEN_TABLE # define YYTOKEN_TABLE 0 #endif #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED typedef union YYSTYPE #line 54 "ael.y" { int intval; /* integer value, typically flags */ char *str; /* strings */ struct pval *pval; /* full objects */ } /* Line 198 of yacc.c. */ #line 236 "ael.tab.c" YYSTYPE; # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 # define YYSTYPE_IS_TRIVIAL 1 #endif #if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED typedef struct YYLTYPE { int first_line; int first_column; int last_line; int last_column; } YYLTYPE; # define yyltype YYLTYPE /* obsolescent; will be withdrawn */ # define YYLTYPE_IS_DECLARED 1 # define YYLTYPE_IS_TRIVIAL 1 #endif /* Copy the second part of user declarations. */ #line 60 "ael.y" /* declaring these AFTER the union makes things a lot simpler! */ void yyerror(YYLTYPE *locp, struct parse_io *parseio, char const *s); int ael_yylex (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , void * yyscanner); /* create a new object with start-end marker */ static pval *npval(pvaltype type, int first_line, int last_line, int first_column, int last_column); /* create a new object with start-end marker, simplified interface. * Must be declared here because YYLTYPE is not known before */ static pval *npval2(pvaltype type, YYLTYPE *first, YYLTYPE *last); /* another frontend for npval, this time for a string */ static pval *nword(char *string, YYLTYPE *pos); /* update end position of an object, return the object */ static pval *update_last(pval *, YYLTYPE *); /* Line 221 of yacc.c. */ #line 281 "ael.tab.c" #ifdef short # undef short #endif #ifdef YYTYPE_UINT8 typedef YYTYPE_UINT8 yytype_uint8; #else typedef unsigned char yytype_uint8; #endif #ifdef YYTYPE_INT8 typedef YYTYPE_INT8 yytype_int8; #elif (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) typedef signed char yytype_int8; #else typedef short int yytype_int8; #endif #ifdef YYTYPE_UINT16 typedef YYTYPE_UINT16 yytype_uint16; #else typedef unsigned short int yytype_uint16; #endif #ifdef YYTYPE_INT16 typedef YYTYPE_INT16 yytype_int16; #else typedef short int yytype_int16; #endif #ifndef YYSIZE_T # ifdef __SIZE_TYPE__ # define YYSIZE_T __SIZE_TYPE__ # elif defined size_t # define YYSIZE_T size_t # elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) # include /* INFRINGES ON USER NAME SPACE */ # define YYSIZE_T size_t # else # define YYSIZE_T unsigned int # endif #endif #define YYSIZE_MAXIMUM ((YYSIZE_T) -1) #ifndef YY_ # if YYENABLE_NLS # if ENABLE_NLS # include /* INFRINGES ON USER NAME SPACE */ # define YY_(msgid) dgettext ("bison-runtime", msgid) # endif # endif # ifndef YY_ # define YY_(msgid) msgid # endif #endif /* Suppress unused-variable warnings by "using" E. */ #if ! defined lint || defined __GNUC__ # define YYUSE(e) ((void) (e)) #else # define YYUSE(e) /* empty */ #endif /* Identity function, used to suppress warnings about constant conditions. */ #ifndef lint # define YYID(n) (n) #else #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static int YYID (int i) #else static int YYID (i) int i; #endif { return i; } #endif #if ! defined yyoverflow || YYERROR_VERBOSE /* The parser invokes alloca or malloc; define the necessary symbols. */ # ifdef YYSTACK_USE_ALLOCA # if YYSTACK_USE_ALLOCA # ifdef __GNUC__ # define YYSTACK_ALLOC __builtin_alloca # elif defined __BUILTIN_VA_ARG_INCR # include /* INFRINGES ON USER NAME SPACE */ # elif defined _AIX # define YYSTACK_ALLOC __alloca # elif defined _MSC_VER # include /* INFRINGES ON USER NAME SPACE */ # define alloca _alloca # else # define YYSTACK_ALLOC alloca # if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) # include /* INFRINGES ON USER NAME SPACE */ # ifndef _STDLIB_H # define _STDLIB_H 1 # endif # endif # endif # endif # endif # ifdef YYSTACK_ALLOC /* Pacify GCC's `empty if-body' warning. */ # define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) # ifndef YYSTACK_ALLOC_MAXIMUM /* The OS might guarantee only one guard page at the bottom of the stack, and a page size can be as small as 4096 bytes. So we cannot safely invoke alloca (N) if N exceeds 4096. Use a slightly smaller number to allow for a few compiler-allocated temporary stack slots. */ # define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ # endif # else # define YYSTACK_ALLOC YYMALLOC # define YYSTACK_FREE YYFREE # ifndef YYSTACK_ALLOC_MAXIMUM # define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM # endif # ifdef __cplusplus extern "C" { # endif # ifndef YYMALLOC # define YYMALLOC malloc # if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ # endif # endif # ifndef YYFREE # define YYFREE free # if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void free (void *); /* INFRINGES ON USER NAME SPACE */ # endif # endif # ifdef __cplusplus } # endif # endif #endif /* ! defined yyoverflow || YYERROR_VERBOSE */ #if (! defined yyoverflow \ && (! defined __cplusplus \ || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \ && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) /* A type that is properly aligned for any stack member. */ union yyalloc { yytype_int16 yyss; YYSTYPE yyvs; YYLTYPE yyls; }; /* The size of the maximum gap between one aligned stack and the next. */ # define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) /* The size of an array large to enough to hold all stacks, each with N elements. */ # define YYSTACK_BYTES(N) \ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE) + sizeof (YYLTYPE)) \ + 2 * YYSTACK_GAP_MAXIMUM) /* Copy COUNT objects from FROM to TO. The source and destination do not overlap. */ # ifndef YYCOPY # if defined __GNUC__ && 1 < __GNUC__ # define YYCOPY(To, From, Count) \ __builtin_memcpy (To, From, (Count) * sizeof (*(From))) # else # define YYCOPY(To, From, Count) \ do \ { \ YYSIZE_T yyi; \ for (yyi = 0; yyi < (Count); yyi++) \ (To)[yyi] = (From)[yyi]; \ } \ while (YYID (0)) # endif # endif /* Relocate STACK from its old location to the new one. The local variables YYSIZE and YYSTACKSIZE give the old and new number of elements in the stack, and YYPTR gives the new location of the stack. Advance YYPTR to a properly aligned location for the next stack. */ # define YYSTACK_RELOCATE(Stack) \ do \ { \ YYSIZE_T yynewbytes; \ YYCOPY (&yyptr->Stack, Stack, yysize); \ Stack = &yyptr->Stack; \ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ yyptr += yynewbytes / sizeof (*yyptr); \ } \ while (YYID (0)) #endif /* YYFINAL -- State number of the termination state. */ #define YYFINAL 17 /* YYLAST -- Last index in YYTABLE. */ #define YYLAST 311 /* YYNTOKENS -- Number of terminals. */ #define YYNTOKENS 43 /* YYNNTS -- Number of nonterminals. */ #define YYNNTS 54 /* YYNRULES -- Number of rules. */ #define YYNRULES 137 /* YYNRULES -- Number of states. */ #define YYNSTATES 276 /* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ #define YYUNDEFTOK 2 #define YYMAXUTOK 297 #define YYTRANSLATE(YYX) \ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) /* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ static const yytype_uint8 yytranslate[] = { 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42 }; #if YYDEBUG /* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in YYRHS. */ static const yytype_uint16 yyprhs[] = { 0, 0, 3, 5, 7, 10, 13, 15, 17, 19, 21, 23, 25, 32, 34, 35, 37, 40, 43, 52, 57, 58, 61, 64, 65, 71, 72, 74, 78, 81, 82, 85, 88, 90, 92, 94, 96, 98, 100, 103, 105, 110, 114, 119, 127, 136, 137, 140, 143, 149, 151, 159, 160, 165, 168, 171, 176, 178, 181, 183, 186, 190, 194, 196, 199, 203, 205, 208, 212, 218, 222, 224, 228, 232, 235, 236, 237, 238, 251, 255, 257, 261, 264, 267, 268, 274, 277, 280, 283, 287, 289, 292, 293, 295, 299, 303, 309, 315, 321, 327, 328, 331, 334, 339, 340, 346, 350, 351, 355, 359, 362, 364, 365, 367, 368, 372, 373, 376, 381, 385, 390, 391, 394, 396, 398, 404, 409, 414, 415, 419, 425, 428, 430, 434, 437, 441, 444, 449 }; /* YYRHS -- A `-1'-separated list of the rules' RHS. */ static const yytype_int8 yyrhs[] = { 44, 0, -1, 45, -1, 46, -1, 45, 46, -1, 45, 1, -1, 48, -1, 50, -1, 51, -1, 8, -1, 42, -1, 37, -1, 49, 3, 47, 4, 56, 5, -1, 23, -1, -1, 24, -1, 24, 23, -1, 23, 24, -1, 15, 42, 6, 55, 7, 4, 89, 5, -1, 16, 4, 52, 5, -1, -1, 53, 52, -1, 1, 52, -1, -1, 42, 9, 54, 42, 8, -1, -1, 42, -1, 55, 10, 42, -1, 55, 1, -1, -1, 57, 56, -1, 1, 56, -1, 59, -1, 96, -1, 91, -1, 92, -1, 58, -1, 53, -1, 42, 1, -1, 8, -1, 17, 25, 42, 8, -1, 42, 25, 71, -1, 31, 42, 25, 71, -1, 32, 6, 67, 7, 42, 25, 71, -1, 31, 32, 6, 67, 7, 42, 25, 71, -1, -1, 71, 60, -1, 1, 60, -1, 68, 11, 68, 11, 68, -1, 42, -1, 61, 13, 68, 13, 68, 13, 68, -1, -1, 6, 64, 66, 7, -1, 19, 63, -1, 22, 63, -1, 20, 6, 62, 7, -1, 42, -1, 42, 42, -1, 42, -1, 67, 42, -1, 67, 11, 42, -1, 67, 12, 42, -1, 42, -1, 42, 42, -1, 42, 42, 42, -1, 42, -1, 42, 42, -1, 69, 11, 42, -1, 18, 63, 4, 87, 5, -1, 4, 60, 5, -1, 53, -1, 26, 77, 8, -1, 27, 79, 8, -1, 42, 11, -1, -1, -1, -1, 33, 6, 72, 42, 8, 73, 42, 8, 74, 42, 7, 71, -1, 34, 63, 71, -1, 70, -1, 12, 80, 8, -1, 84, 8, -1, 42, 8, -1, -1, 84, 9, 75, 42, 8, -1, 29, 8, -1, 28, 8, -1, 30, 8, -1, 65, 71, 76, -1, 8, -1, 21, 71, -1, -1, 69, -1, 69, 13, 69, -1, 69, 10, 69, -1, 69, 13, 69, 13, 69, -1, 69, 10, 69, 10, 69, -1, 37, 13, 69, 13, 69, -1, 37, 10, 69, 10, 69, -1, -1, 10, 42, -1, 69, 78, -1, 69, 78, 14, 47, -1, -1, 42, 6, 81, 86, 7, -1, 42, 6, 7, -1, -1, 42, 6, 83, -1, 82, 86, 7, -1, 82, 7, -1, 42, -1, -1, 66, -1, -1, 86, 10, 85, -1, -1, 88, 87, -1, 35, 42, 11, 60, -1, 37, 11, 60, -1, 36, 42, 11, 60, -1, -1, 90, 89, -1, 71, -1, 96, -1, 38, 42, 4, 60, 5, -1, 39, 4, 93, 5, -1, 40, 4, 93, 5, -1, -1, 42, 8, 93, -1, 42, 14, 42, 8, 93, -1, 1, 93, -1, 47, -1, 47, 13, 62, -1, 94, 8, -1, 95, 94, 8, -1, 95, 1, -1, 41, 4, 95, 5, -1, 41, 4, 5, -1 }; /* YYRLINE[YYN] -- source line where rule number YYN was defined. */ static const yytype_uint16 yyrline[] = { 0, 185, 185, 188, 189, 190, 193, 194, 195, 196, 199, 200, 203, 212, 213, 214, 215, 216, 219, 225, 231, 232, 233, 236, 236, 243, 244, 245, 246, 249, 250, 251, 254, 255, 256, 257, 258, 259, 260, 261, 264, 269, 273, 278, 283, 293, 294, 295, 301, 306, 310, 318, 318, 322, 325, 328, 339, 340, 347, 348, 352, 356, 362, 363, 368, 376, 377, 381, 387, 396, 399, 400, 403, 406, 409, 410, 411, 409, 417, 421, 422, 423, 424, 427, 427, 460, 461, 462, 463, 467, 470, 471, 474, 475, 478, 481, 485, 489, 493, 499, 500, 504, 507, 513, 513, 518, 526, 526, 537, 544, 547, 548, 551, 552, 555, 558, 559, 562, 566, 570, 576, 577, 580, 581, 582, 588, 593, 598, 599, 600, 602, 605, 606, 613, 614, 615, 618, 621 }; #endif #if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE /* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. First, the terminals, then, starting at YYNTOKENS, nonterminals. */ static const char *const yytname[] = { "$end", "error", "$undefined", "KW_CONTEXT", "LC", "RC", "LP", "RP", "SEMI", "EQ", "COMMA", "COLON", "AMPER", "BAR", "AT", "KW_MACRO", "KW_GLOBALS", "KW_IGNOREPAT", "KW_SWITCH", "KW_IF", "KW_IFTIME", "KW_ELSE", "KW_RANDOM", "KW_ABSTRACT", "KW_EXTEND", "EXTENMARK", "KW_GOTO", "KW_JUMP", "KW_RETURN", "KW_BREAK", "KW_CONTINUE", "KW_REGEXTEN", "KW_HINT", "KW_FOR", "KW_WHILE", "KW_CASE", "KW_PATTERN", "KW_DEFAULT", "KW_CATCH", "KW_SWITCHES", "KW_ESWITCHES", "KW_INCLUDES", "word", "$accept", "file", "objects", "object", "context_name", "context", "opt_abstract", "macro", "globals", "global_statements", "assignment", "@1", "arglist", "elements", "element", "ignorepat", "extension", "statements", "timerange", "timespec", "test_expr", "@2", "if_like_head", "word_list", "hint_word", "word3_list", "goto_word", "switch_statement", "statement", "@3", "@4", "@5", "@6", "opt_else", "target", "opt_pri", "jumptarget", "macro_call", "@7", "application_call_head", "@8", "application_call", "opt_word", "eval_arglist", "case_statements", "case_statement", "macro_statements", "macro_statement", "switches", "eswitches", "switchlist", "included_entry", "includeslist", "includes", 0 }; #endif # ifdef YYPRINT /* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to token YYLEX-NUM. */ static const yytype_uint16 yytoknum[] = { 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297 }; # endif /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ static const yytype_uint8 yyr1[] = { 0, 43, 44, 45, 45, 45, 46, 46, 46, 46, 47, 47, 48, 49, 49, 49, 49, 49, 50, 51, 52, 52, 52, 54, 53, 55, 55, 55, 55, 56, 56, 56, 57, 57, 57, 57, 57, 57, 57, 57, 58, 59, 59, 59, 59, 60, 60, 60, 61, 61, 62, 64, 63, 65, 65, 65, 66, 66, 67, 67, 67, 67, 68, 68, 68, 69, 69, 69, 70, 71, 71, 71, 71, 71, 72, 73, 74, 71, 71, 71, 71, 71, 71, 75, 71, 71, 71, 71, 71, 71, 76, 76, 77, 77, 77, 77, 77, 77, 77, 78, 78, 79, 79, 81, 80, 80, 83, 82, 84, 84, 85, 85, 86, 86, 86, 87, 87, 88, 88, 88, 89, 89, 90, 90, 90, 91, 92, 93, 93, 93, 93, 94, 94, 95, 95, 95, 96, 96 }; /* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ static const yytype_uint8 yyr2[] = { 0, 2, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 6, 1, 0, 1, 2, 2, 8, 4, 0, 2, 2, 0, 5, 0, 1, 3, 2, 0, 2, 2, 1, 1, 1, 1, 1, 1, 2, 1, 4, 3, 4, 7, 8, 0, 2, 2, 5, 1, 7, 0, 4, 2, 2, 4, 1, 2, 1, 2, 3, 3, 1, 2, 3, 1, 2, 3, 5, 3, 1, 3, 3, 2, 0, 0, 0, 12, 3, 1, 3, 2, 2, 0, 5, 2, 2, 2, 3, 1, 2, 0, 1, 3, 3, 5, 5, 5, 5, 0, 2, 2, 4, 0, 5, 3, 0, 3, 3, 2, 1, 0, 1, 0, 3, 0, 2, 4, 3, 4, 0, 2, 1, 1, 5, 4, 4, 0, 3, 5, 2, 1, 3, 2, 3, 2, 4, 3 }; /* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state STATE-NUM when YYTABLE doesn't specify something else to do. Zero means the default is an error. */ static const yytype_uint8 yydefact[] = { 14, 9, 0, 0, 13, 15, 0, 0, 3, 6, 0, 7, 8, 0, 0, 17, 16, 1, 5, 4, 0, 25, 0, 0, 0, 0, 11, 10, 0, 26, 0, 22, 23, 19, 21, 0, 28, 0, 0, 0, 0, 39, 0, 0, 0, 0, 0, 0, 0, 37, 0, 0, 36, 32, 34, 35, 33, 120, 27, 0, 31, 0, 0, 0, 0, 0, 0, 0, 38, 0, 12, 30, 0, 89, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 79, 122, 113, 0, 0, 120, 123, 24, 0, 0, 0, 58, 0, 0, 0, 0, 0, 137, 131, 0, 0, 41, 0, 0, 0, 0, 0, 51, 0, 53, 0, 54, 0, 65, 92, 0, 99, 0, 86, 85, 87, 74, 0, 0, 106, 82, 73, 91, 109, 56, 112, 0, 81, 83, 18, 121, 40, 0, 42, 0, 0, 0, 59, 130, 0, 0, 125, 126, 0, 133, 135, 136, 0, 47, 69, 46, 103, 80, 0, 115, 49, 0, 0, 0, 0, 0, 66, 0, 0, 0, 71, 0, 101, 72, 0, 78, 0, 107, 0, 88, 57, 108, 111, 0, 0, 0, 60, 61, 128, 0, 132, 134, 105, 113, 0, 0, 0, 0, 0, 115, 63, 0, 55, 0, 0, 0, 94, 67, 93, 100, 0, 0, 0, 90, 110, 114, 0, 0, 0, 0, 0, 52, 0, 0, 0, 68, 116, 64, 62, 0, 0, 0, 0, 0, 0, 102, 75, 124, 84, 0, 43, 129, 104, 0, 0, 118, 0, 0, 98, 97, 96, 95, 0, 44, 117, 119, 0, 48, 0, 0, 76, 50, 0, 0, 0, 77 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int16 yydefgoto[] = { -1, 6, 7, 8, 108, 9, 10, 11, 12, 24, 88, 39, 30, 50, 51, 52, 53, 113, 171, 172, 118, 168, 89, 140, 102, 173, 124, 90, 114, 184, 262, 272, 193, 189, 125, 182, 127, 116, 203, 92, 187, 93, 225, 141, 208, 209, 94, 95, 54, 55, 105, 109, 110, 56 }; /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ #define YYPACT_NINF -206 static const yytype_int16 yypact[] = { 65, -206, 2, 64, -1, 70, 84, 231, -206, -206, 108, -206, -206, 120, 13, -206, -206, -206, -206, -206, 29, 88, 13, 130, 163, 13, -206, -206, 165, -206, 75, -206, -206, -206, -206, 8, -206, 171, 134, 136, 8, -206, 154, -22, 178, 182, 186, 189, 61, -206, 200, 8, -206, -206, -206, -206, -206, 169, -206, 198, -206, 152, 203, 187, 177, 14, 14, 23, -206, 196, -206, -206, 128, -206, 179, 207, 207, 214, 207, 58, 185, 220, 225, 227, 230, 207, 195, 174, -206, 196, -206, -206, 17, 94, 235, 169, -206, -206, 233, 177, 196, -206, 19, 14, 55, 237, 238, -206, 232, 236, 16, -206, 86, 243, 86, 244, 241, -206, 247, -206, 210, -206, 97, 211, 24, 248, 107, 249, -206, -206, -206, -206, 196, 254, -206, -206, -206, 239, -206, 217, -206, 117, -206, -206, -206, -206, -206, 67, -206, 219, 221, 222, -206, -206, 14, 223, -206, -206, 210, -206, -206, -206, 258, -206, -206, -206, 255, -206, 226, 106, 0, 256, 260, 259, 185, 185, -206, 185, 229, 185, -206, 234, 261, -206, 240, -206, 128, -206, 196, -206, -206, -206, 242, 245, 246, 252, -206, -206, -206, 264, -206, -206, -206, 226, 266, 250, 251, 263, 273, 106, 253, 257, -206, 257, 142, 138, 149, -206, 161, -206, 29, 271, 275, -206, -206, -206, 277, 265, 196, 14, 127, -206, 270, 272, 86, -206, -206, -206, 262, 276, 280, 185, 185, 185, 185, -206, -206, -206, -206, 196, -206, -206, -206, 86, 86, -206, 257, 257, 283, 283, 283, 283, 267, -206, -206, -206, 284, -206, 278, 257, -206, -206, 268, 289, 196, -206 }; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int16 yypgoto[] = { -206, -206, -206, 291, -19, -206, -206, -206, -206, 113, 32, -206, -206, -18, -206, -206, -206, -109, -206, 143, -49, -206, -206, 132, 204, -205, -78, -206, -57, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, 99, 96, -206, 212, -206, -206, -206, -62, 201, -206, -50 }; /* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule which number is the opposite. If zero, do what YYDEFACT says. If YYTABLE_NINF, syntax error. */ #define YYTABLE_NINF -128 static const yytype_int16 yytable[] = { 91, 28, 126, 163, 106, 165, 239, 96, 240, 40, 62, -62, 111, -29, 22, 103, 41, 160, -20, -127, 63, 161, 60, 15, 138, 42, 149, 119, 107, 121, 150, 151, 137, 71, 177, 178, 132, 179, 91, 43, 44, 153, 210, 148, 13, 96, 25, 45, 46, 47, 48, 266, 267, 26, 25, 23, 104, 25, 27, 139, 26, 152, 68, 154, 271, 27, 26, 49, 14, 155, 32, 27, 49, 1, 194, 185, 36, 222, 150, 151, 2, 3, 37, 49, 17, 38, 69, 112, 4, 5, 72, -45, 198, 16, 73, 122, 214, 215, 74, 216, 123, 218, 142, 143, 75, 76, 77, 174, 78, 152, 175, 20, 79, 80, 81, 82, 83, 181, 178, 84, 85, -45, -45, -45, 191, 255, 21, 192, 87, 112, 29, 223, 72, -45, 252, 31, 73, 192, 34, 32, 74, 205, 206, 207, 264, 265, 75, 76, 77, 178, 78, 242, 241, 178, 79, 80, 81, 82, 83, 243, 178, 84, 85, 258, 259, 260, 261, 251, 33, 35, 87, 250, 178, 72, 244, 57, 58, 73, 59, 61, 134, 74, 135, 32, 64, 136, 65, 75, 76, 77, 66, 78, 263, 67, 98, 79, 80, 81, 82, 83, 72, 245, 84, 85, 73, 70, 97, 86, 74, 99, 47, 87, 100, 117, 75, 76, 77, 275, 78, 101, 120, 115, 79, 80, 81, 82, 83, 123, 128, 84, 85, -2, 18, 129, -14, 130, 131, 133, 87, 1, 144, 146, 156, 157, 159, 158, 2, 3, 164, 167, 166, 169, 170, 176, 4, 5, 180, 183, 186, 190, 188, 195, 202, 196, 197, 199, 201, 212, 139, 211, 213, 217, 229, 231, 234, 220, 219, 228, 235, 246, 247, 253, 221, 254, 224, 248, 270, 226, 227, 256, 249, 257, 232, 233, 178, 237, 274, 269, 19, 238, 204, 200, 230, 147, 210, 236, 0, 145, 0, 268, 273, 162 }; static const yytype_int16 yycheck[] = { 57, 20, 80, 112, 66, 114, 211, 57, 213, 1, 32, 11, 69, 5, 1, 1, 8, 1, 5, 5, 42, 5, 40, 24, 7, 17, 7, 76, 5, 78, 11, 12, 89, 51, 10, 11, 85, 13, 95, 31, 32, 103, 42, 100, 42, 95, 14, 39, 40, 41, 42, 256, 257, 37, 22, 42, 42, 25, 42, 42, 37, 42, 1, 8, 269, 42, 37, 35, 4, 14, 9, 42, 40, 8, 7, 132, 1, 186, 11, 12, 15, 16, 7, 51, 0, 10, 25, 1, 23, 24, 4, 5, 154, 23, 8, 37, 174, 175, 12, 177, 42, 179, 8, 9, 18, 19, 20, 10, 22, 42, 13, 3, 26, 27, 28, 29, 30, 10, 11, 33, 34, 35, 36, 37, 7, 234, 6, 10, 42, 1, 42, 188, 4, 5, 7, 22, 8, 10, 25, 9, 12, 35, 36, 37, 253, 254, 18, 19, 20, 11, 22, 13, 10, 11, 26, 27, 28, 29, 30, 10, 11, 33, 34, 241, 242, 243, 244, 229, 5, 4, 42, 228, 11, 4, 13, 4, 42, 8, 42, 25, 6, 12, 8, 9, 6, 11, 4, 18, 19, 20, 4, 22, 249, 4, 42, 26, 27, 28, 29, 30, 4, 220, 33, 34, 8, 5, 8, 38, 12, 6, 41, 42, 25, 6, 18, 19, 20, 274, 22, 42, 6, 42, 26, 27, 28, 29, 30, 42, 8, 33, 34, 0, 1, 8, 3, 8, 6, 42, 42, 8, 5, 8, 5, 5, 8, 13, 15, 16, 5, 8, 6, 4, 42, 42, 23, 24, 8, 8, 4, 42, 21, 42, 7, 42, 42, 42, 8, 7, 42, 13, 11, 42, 8, 7, 11, 14, 42, 25, 5, 8, 5, 11, 42, 11, 42, 8, 8, 42, 42, 13, 25, 11, 42, 42, 11, 42, 7, 13, 7, 42, 168, 158, 203, 99, 42, 209, -1, 95, -1, 42, 42, 110 }; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing symbol of state STATE-NUM. */ static const yytype_uint8 yystos[] = { 0, 8, 15, 16, 23, 24, 44, 45, 46, 48, 49, 50, 51, 42, 4, 24, 23, 0, 1, 46, 3, 6, 1, 42, 52, 53, 37, 42, 47, 42, 55, 52, 9, 5, 52, 4, 1, 7, 10, 54, 1, 8, 17, 31, 32, 39, 40, 41, 42, 53, 56, 57, 58, 59, 91, 92, 96, 4, 42, 42, 56, 25, 32, 42, 6, 4, 4, 4, 1, 25, 5, 56, 4, 8, 12, 18, 19, 20, 22, 26, 27, 28, 29, 30, 33, 34, 38, 42, 53, 65, 70, 71, 82, 84, 89, 90, 96, 8, 42, 6, 25, 42, 67, 1, 42, 93, 93, 5, 47, 94, 95, 71, 1, 60, 71, 42, 80, 6, 63, 63, 6, 63, 37, 42, 69, 77, 69, 79, 8, 8, 8, 6, 63, 42, 6, 8, 11, 71, 7, 42, 66, 86, 8, 9, 5, 89, 8, 67, 71, 7, 11, 12, 42, 93, 8, 14, 5, 5, 13, 8, 1, 5, 94, 60, 5, 60, 6, 8, 64, 4, 42, 61, 62, 68, 10, 13, 42, 10, 11, 13, 8, 10, 78, 8, 72, 71, 4, 83, 21, 76, 42, 7, 10, 75, 7, 42, 42, 42, 93, 42, 62, 8, 7, 81, 66, 35, 36, 37, 87, 88, 42, 13, 7, 11, 69, 69, 69, 42, 69, 42, 14, 42, 60, 71, 42, 85, 42, 42, 25, 8, 86, 7, 42, 42, 11, 5, 87, 42, 42, 68, 68, 10, 13, 10, 13, 47, 8, 5, 8, 25, 71, 93, 7, 11, 11, 60, 13, 11, 69, 69, 69, 69, 73, 71, 60, 60, 68, 68, 42, 13, 8, 68, 74, 42, 7, 71 }; #define yyerrok (yyerrstatus = 0) #define yyclearin (yychar = YYEMPTY) #define YYEMPTY (-2) #define YYEOF 0 #define YYACCEPT goto yyacceptlab #define YYABORT goto yyabortlab #define YYERROR goto yyerrorlab /* Like YYERROR except do call yyerror. This remains here temporarily to ease the transition to the new meaning of YYERROR, for GCC. Once GCC version 2 has supplanted version 1, this can go. */ #define YYFAIL goto yyerrlab #define YYRECOVERING() (!!yyerrstatus) #define YYBACKUP(Token, Value) \ do \ if (yychar == YYEMPTY && yylen == 1) \ { \ yychar = (Token); \ yylval = (Value); \ yytoken = YYTRANSLATE (yychar); \ YYPOPSTACK (1); \ goto yybackup; \ } \ else \ { \ yyerror (&yylloc, parseio, YY_("syntax error: cannot back up")); \ YYERROR; \ } \ while (YYID (0)) #define YYTERROR 1 #define YYERRCODE 256 /* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. If N is 0, then set CURRENT to the empty location which ends the previous symbol: RHS[0] (always defined). */ #define YYRHSLOC(Rhs, K) ((Rhs)[K]) #ifndef YYLLOC_DEFAULT # define YYLLOC_DEFAULT(Current, Rhs, N) \ do \ if (YYID (N)) \ { \ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ } \ else \ { \ (Current).first_line = (Current).last_line = \ YYRHSLOC (Rhs, 0).last_line; \ (Current).first_column = (Current).last_column = \ YYRHSLOC (Rhs, 0).last_column; \ } \ while (YYID (0)) #endif /* YY_LOCATION_PRINT -- Print the location on the stream. This macro was not mandated originally: define only if we know we won't break user code: when these are the locations we know. */ #ifndef YY_LOCATION_PRINT # if YYLTYPE_IS_TRIVIAL # define YY_LOCATION_PRINT(File, Loc) \ fprintf (File, "%d.%d-%d.%d", \ (Loc).first_line, (Loc).first_column, \ (Loc).last_line, (Loc).last_column) # else # define YY_LOCATION_PRINT(File, Loc) ((void) 0) # endif #endif /* YYLEX -- calling `yylex' with the right arguments. */ #ifdef YYLEX_PARAM # define YYLEX yylex (&yylval, &yylloc, YYLEX_PARAM) #else # define YYLEX yylex (&yylval, &yylloc) #endif /* Enable debugging if requested. */ #if YYDEBUG # ifndef YYFPRINTF # include /* INFRINGES ON USER NAME SPACE */ # define YYFPRINTF fprintf # endif # define YYDPRINTF(Args) \ do { \ if (yydebug) \ YYFPRINTF Args; \ } while (YYID (0)) # define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ do { \ if (yydebug) \ { \ YYFPRINTF (stderr, "%s ", Title); \ yy_symbol_print (stderr, \ Type, Value, Location, parseio); \ YYFPRINTF (stderr, "\n"); \ } \ } while (YYID (0)) /*--------------------------------. | Print this symbol on YYOUTPUT. | `--------------------------------*/ /*ARGSUSED*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_symbol_value_print (FILE *yyoutput, int yytype, const YYSTYPE * const yyvaluep, const YYLTYPE * const yylocationp, struct parse_io *parseio) #else static void yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, parseio) FILE *yyoutput; int yytype; const YYSTYPE * const yyvaluep; const YYLTYPE * const yylocationp; struct parse_io *parseio; #endif { if (!yyvaluep) return; YYUSE (yylocationp); YYUSE (parseio); # ifdef YYPRINT if (yytype < YYNTOKENS) YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); # else YYUSE (yyoutput); # endif switch (yytype) { default: break; } } /*--------------------------------. | Print this symbol on YYOUTPUT. | `--------------------------------*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_symbol_print (FILE *yyoutput, int yytype, const YYSTYPE * const yyvaluep, const YYLTYPE * const yylocationp, struct parse_io *parseio) #else static void yy_symbol_print (yyoutput, yytype, yyvaluep, yylocationp, parseio) FILE *yyoutput; int yytype; const YYSTYPE * const yyvaluep; const YYLTYPE * const yylocationp; struct parse_io *parseio; #endif { if (yytype < YYNTOKENS) YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); else YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); YY_LOCATION_PRINT (yyoutput, *yylocationp); YYFPRINTF (yyoutput, ": "); yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, parseio); YYFPRINTF (yyoutput, ")"); } /*------------------------------------------------------------------. | yy_stack_print -- Print the state stack from its BOTTOM up to its | | TOP (included). | `------------------------------------------------------------------*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_stack_print (yytype_int16 *bottom, yytype_int16 *top) #else static void yy_stack_print (bottom, top) yytype_int16 *bottom; yytype_int16 *top; #endif { YYFPRINTF (stderr, "Stack now"); for (; bottom <= top; ++bottom) YYFPRINTF (stderr, " %d", *bottom); YYFPRINTF (stderr, "\n"); } # define YY_STACK_PRINT(Bottom, Top) \ do { \ if (yydebug) \ yy_stack_print ((Bottom), (Top)); \ } while (YYID (0)) /*------------------------------------------------. | Report that the YYRULE is going to be reduced. | `------------------------------------------------*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_reduce_print (YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule, struct parse_io *parseio) #else static void yy_reduce_print (yyvsp, yylsp, yyrule, parseio) YYSTYPE *yyvsp; YYLTYPE *yylsp; int yyrule; struct parse_io *parseio; #endif { int yynrhs = yyr2[yyrule]; int yyi; unsigned long int yylno = yyrline[yyrule]; YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", yyrule - 1, yylno); /* The symbols being reduced. */ for (yyi = 0; yyi < yynrhs; yyi++) { fprintf (stderr, " $%d = ", yyi + 1); yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], &(yyvsp[(yyi + 1) - (yynrhs)]) , &(yylsp[(yyi + 1) - (yynrhs)]) , parseio); fprintf (stderr, "\n"); } } # define YY_REDUCE_PRINT(Rule) \ do { \ if (yydebug) \ yy_reduce_print (yyvsp, yylsp, Rule, parseio); \ } while (YYID (0)) /* Nonzero means print parse trace. It is left uninitialized so that multiple parsers can coexist. */ int yydebug; #else /* !YYDEBUG */ # define YYDPRINTF(Args) # define YY_SYMBOL_PRINT(Title, Type, Value, Location) # define YY_STACK_PRINT(Bottom, Top) # define YY_REDUCE_PRINT(Rule) #endif /* !YYDEBUG */ /* YYINITDEPTH -- initial size of the parser's stacks. */ #ifndef YYINITDEPTH # define YYINITDEPTH 200 #endif /* YYMAXDEPTH -- maximum size the stacks can grow to (effective only if the built-in stack extension method is used). Do not make this value too large; the results are undefined if YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) evaluated with infinite-precision integer arithmetic. */ #ifndef YYMAXDEPTH # define YYMAXDEPTH 10000 #endif #if YYERROR_VERBOSE # ifndef yystrlen # if defined __GLIBC__ && defined _STRING_H # define yystrlen strlen # else /* Return the length of YYSTR. */ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static YYSIZE_T yystrlen (const char *yystr) #else static YYSIZE_T yystrlen (yystr) const char *yystr; #endif { YYSIZE_T yylen; for (yylen = 0; yystr[yylen]; yylen++) continue; return yylen; } # endif # endif # ifndef yystpcpy # if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE # define yystpcpy stpcpy # else /* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in YYDEST. */ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static char * yystpcpy (char *yydest, const char *yysrc) #else static char * yystpcpy (yydest, yysrc) char *yydest; const char *yysrc; #endif { char *yyd = yydest; const char *yys = yysrc; while ((*yyd++ = *yys++) != '\0') continue; return yyd - 1; } # endif # endif # ifndef yytnamerr /* Copy to YYRES the contents of YYSTR after stripping away unnecessary quotes and backslashes, so that it's suitable for yyerror. The heuristic is that double-quoting is unnecessary unless the string contains an apostrophe, a comma, or backslash (other than backslash-backslash). YYSTR is taken from yytname. If YYRES is null, do not copy; instead, return the length of what the result would have been. */ static YYSIZE_T yytnamerr (char *yyres, const char *yystr) { if (*yystr == '"') { size_t yyn = 0; char const *yyp = yystr; for (;;) switch (*++yyp) { case '\'': case ',': goto do_not_strip_quotes; case '\\': if (*++yyp != '\\') goto do_not_strip_quotes; /* Fall through. */ default: if (yyres) yyres[yyn] = *yyp; yyn++; break; case '"': if (yyres) yyres[yyn] = '\0'; return yyn; } do_not_strip_quotes: ; } if (! yyres) return yystrlen (yystr); return yystpcpy (yyres, yystr) - yyres; } # endif /* Copy into YYRESULT an error message about the unexpected token YYCHAR while in state YYSTATE. Return the number of bytes copied, including the terminating null byte. If YYRESULT is null, do not copy anything; just return the number of bytes that would be copied. As a special case, return 0 if an ordinary "syntax error" message will do. Return YYSIZE_MAXIMUM if overflow occurs during size calculation. */ static YYSIZE_T yysyntax_error (char *yyresult, int yystate, int yychar) { int yyn = yypact[yystate]; if (! (YYPACT_NINF < yyn && yyn < YYLAST)) return 0; else { int yytype = YYTRANSLATE (yychar); YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); YYSIZE_T yysize = yysize0; YYSIZE_T yysize1; int yysize_overflow = 0; enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; int yyx; # if 0 /* This is so xgettext sees the translatable formats that are constructed on the fly. */ YY_("syntax error, unexpected %s"); YY_("syntax error, unexpected %s, expecting %s"); YY_("syntax error, unexpected %s, expecting %s or %s"); YY_("syntax error, unexpected %s, expecting %s or %s or %s"); YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); # endif char *yyfmt; char const *yyf; static char const yyunexpected[] = "syntax error, unexpected %s"; static char const yyexpecting[] = ", expecting %s"; static char const yyor[] = " or %s"; char yyformat[sizeof yyunexpected + sizeof yyexpecting - 1 + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) * (sizeof yyor - 1))]; char const *yyprefix = yyexpecting; /* Start YYX at -YYN if negative to avoid negative indexes in YYCHECK. */ int yyxbegin = yyn < 0 ? -yyn : 0; /* Stay within bounds of both yycheck and yytname. */ int yychecklim = YYLAST - yyn; int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; int yycount = 1; yyarg[0] = yytname[yytype]; yyfmt = yystpcpy (yyformat, yyunexpected); for (yyx = yyxbegin; yyx < yyxend; ++yyx) if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) { if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) { yycount = 1; yysize = yysize0; yyformat[sizeof yyunexpected - 1] = '\0'; break; } yyarg[yycount++] = yytname[yyx]; yysize1 = yysize + yytnamerr (0, yytname[yyx]); yysize_overflow |= (yysize1 < yysize); yysize = yysize1; yyfmt = yystpcpy (yyfmt, yyprefix); yyprefix = yyor; } yyf = YY_(yyformat); yysize1 = yysize + yystrlen (yyf); yysize_overflow |= (yysize1 < yysize); yysize = yysize1; if (yysize_overflow) return YYSIZE_MAXIMUM; if (yyresult) { /* Avoid sprintf, as that infringes on the user's name space. Don't have undefined behavior even if the translation produced a string with the wrong number of "%s"s. */ char *yyp = yyresult; int yyi = 0; while ((*yyp = *yyf) != '\0') { if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) { yyp += yytnamerr (yyp, yyarg[yyi++]); yyf += 2; } else { yyp++; yyf++; } } } return yysize; } } #endif /* YYERROR_VERBOSE */ /*-----------------------------------------------. | Release the memory associated to this symbol. | `-----------------------------------------------*/ /*ARGSUSED*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, struct parse_io *parseio) #else static void yydestruct (yymsg, yytype, yyvaluep, yylocationp, parseio) const char *yymsg; int yytype; YYSTYPE *yyvaluep; YYLTYPE *yylocationp; struct parse_io *parseio; #endif { YYUSE (yyvaluep); YYUSE (yylocationp); YYUSE (parseio); if (!yymsg) yymsg = "Deleting"; YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); switch (yytype) { case 42: /* "word" */ #line 177 "ael.y" { free((yyvaluep->str));}; #line 1443 "ael.tab.c" break; case 45: /* "objects" */ #line 164 "ael.y" { destroy_pval((yyvaluep->pval)); prev_word=0; }; #line 1451 "ael.tab.c" break; case 46: /* "object" */ #line 164 "ael.y" { destroy_pval((yyvaluep->pval)); prev_word=0; }; #line 1459 "ael.tab.c" break; case 47: /* "context_name" */ #line 177 "ael.y" { free((yyvaluep->str));}; #line 1464 "ael.tab.c" break; case 48: /* "context" */ #line 164 "ael.y" { destroy_pval((yyvaluep->pval)); prev_word=0; }; #line 1472 "ael.tab.c" break; case 50: /* "macro" */ #line 164 "ael.y" { destroy_pval((yyvaluep->pval)); prev_word=0; }; #line 1480 "ael.tab.c" break; case 51: /* "globals" */ #line 164 "ael.y" { destroy_pval((yyvaluep->pval)); prev_word=0; }; #line 1488 "ael.tab.c" break; case 52: /* "global_statements" */ #line 164 "ael.y" { destroy_pval((yyvaluep->pval)); prev_word=0; }; #line 1496 "ael.tab.c" break; case 53: /* "assignment" */ #line 164 "ael.y" { destroy_pval((yyvaluep->pval)); prev_word=0; }; #line 1504 "ael.tab.c" break; case 55: /* "arglist" */ #line 164 "ael.y" { destroy_pval((yyvaluep->pval)); prev_word=0; }; #line 1512 "ael.tab.c" break; case 56: /* "elements" */ #line 164 "ael.y" { destroy_pval((yyvaluep->pval)); prev_word=0; }; #line 1520 "ael.tab.c" break; case 57: /* "element" */ #line 164 "ael.y" { destroy_pval((yyvaluep->pval)); prev_word=0; }; #line 1528 "ael.tab.c" break; case 58: /* "ignorepat" */ #line 164 "ael.y" { destroy_pval((yyvaluep->pval)); prev_word=0; }; #line 1536 "ael.tab.c" break; case 59: /* "extension" */ #line 164 "ael.y" { destroy_pval((yyvaluep->pval)); prev_word=0; }; #line 1544 "ael.tab.c" break; case 60: /* "statements" */ #line 164 "ael.y" { destroy_pval((yyvaluep->pval)); prev_word=0; }; #line 1552 "ael.tab.c" break; case 61: /* "timerange" */ #line 177 "ael.y" { free((yyvaluep->str));}; #line 1557 "ael.tab.c" break; case 62: /* "timespec" */ #line 164 "ael.y" { destroy_pval((yyvaluep->pval)); prev_word=0; }; #line 1565 "ael.tab.c" break; case 63: /* "test_expr" */ #line 177 "ael.y" { free((yyvaluep->str));}; #line 1570 "ael.tab.c" break; case 65: /* "if_like_head" */ #line 164 "ael.y" { destroy_pval((yyvaluep->pval)); prev_word=0; }; #line 1578 "ael.tab.c" break; case 66: /* "word_list" */ #line 177 "ael.y" { free((yyvaluep->str));}; #line 1583 "ael.tab.c" break; case 68: /* "word3_list" */ #line 177 "ael.y" { free((yyvaluep->str));}; #line 1588 "ael.tab.c" break; case 69: /* "goto_word" */ #line 177 "ael.y" { free((yyvaluep->str));}; #line 1593 "ael.tab.c" break; case 70: /* "switch_statement" */ #line 164 "ael.y" { destroy_pval((yyvaluep->pval)); prev_word=0; }; #line 1601 "ael.tab.c" break; case 71: /* "statement" */ #line 164 "ael.y" { destroy_pval((yyvaluep->pval)); prev_word=0; }; #line 1609 "ael.tab.c" break; case 76: /* "opt_else" */ #line 164 "ael.y" { destroy_pval((yyvaluep->pval)); prev_word=0; }; #line 1617 "ael.tab.c" break; case 77: /* "target" */ #line 164 "ael.y" { destroy_pval((yyvaluep->pval)); prev_word=0; }; #line 1625 "ael.tab.c" break; case 78: /* "opt_pri" */ #line 177 "ael.y" { free((yyvaluep->str));}; #line 1630 "ael.tab.c" break; case 79: /* "jumptarget" */ #line 164 "ael.y" { destroy_pval((yyvaluep->pval)); prev_word=0; }; #line 1638 "ael.tab.c" break; case 80: /* "macro_call" */ #line 164 "ael.y" { destroy_pval((yyvaluep->pval)); prev_word=0; }; #line 1646 "ael.tab.c" break; case 82: /* "application_call_head" */ #line 164 "ael.y" { destroy_pval((yyvaluep->pval)); prev_word=0; }; #line 1654 "ael.tab.c" break; case 84: /* "application_call" */ #line 164 "ael.y" { destroy_pval((yyvaluep->pval)); prev_word=0; }; #line 1662 "ael.tab.c" break; case 85: /* "opt_word" */ #line 177 "ael.y" { free((yyvaluep->str));}; #line 1667 "ael.tab.c" break; case 86: /* "eval_arglist" */ #line 164 "ael.y" { destroy_pval((yyvaluep->pval)); prev_word=0; }; #line 1675 "ael.tab.c" break; case 87: /* "case_statements" */ #line 164 "ael.y" { destroy_pval((yyvaluep->pval)); prev_word=0; }; #line 1683 "ael.tab.c" break; case 88: /* "case_statement" */ #line 164 "ael.y" { destroy_pval((yyvaluep->pval)); prev_word=0; }; #line 1691 "ael.tab.c" break; case 89: /* "macro_statements" */ #line 164 "ael.y" { destroy_pval((yyvaluep->pval)); prev_word=0; }; #line 1699 "ael.tab.c" break; case 90: /* "macro_statement" */ #line 164 "ael.y" { destroy_pval((yyvaluep->pval)); prev_word=0; }; #line 1707 "ael.tab.c" break; case 91: /* "switches" */ #line 164 "ael.y" { destroy_pval((yyvaluep->pval)); prev_word=0; }; #line 1715 "ael.tab.c" break; case 92: /* "eswitches" */ #line 164 "ael.y" { destroy_pval((yyvaluep->pval)); prev_word=0; }; #line 1723 "ael.tab.c" break; case 93: /* "switchlist" */ #line 164 "ael.y" { destroy_pval((yyvaluep->pval)); prev_word=0; }; #line 1731 "ael.tab.c" break; case 94: /* "included_entry" */ #line 164 "ael.y" { destroy_pval((yyvaluep->pval)); prev_word=0; }; #line 1739 "ael.tab.c" break; case 95: /* "includeslist" */ #line 164 "ael.y" { destroy_pval((yyvaluep->pval)); prev_word=0; }; #line 1747 "ael.tab.c" break; case 96: /* "includes" */ #line 164 "ael.y" { destroy_pval((yyvaluep->pval)); prev_word=0; }; #line 1755 "ael.tab.c" break; default: break; } } /* Prevent warnings from -Wmissing-prototypes. */ #ifdef YYPARSE_PARAM #if defined __STDC__ || defined __cplusplus int yyparse (void *YYPARSE_PARAM); #else int yyparse (); #endif #else /* ! YYPARSE_PARAM */ #if defined __STDC__ || defined __cplusplus int yyparse (struct parse_io *parseio); #else int yyparse (); #endif #endif /* ! YYPARSE_PARAM */ /*----------. | yyparse. | `----------*/ #ifdef YYPARSE_PARAM #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) int yyparse (void *YYPARSE_PARAM) #else int yyparse (YYPARSE_PARAM) void *YYPARSE_PARAM; #endif #else /* ! YYPARSE_PARAM */ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) int yyparse (struct parse_io *parseio) #else int yyparse (parseio) struct parse_io *parseio; #endif #endif { /* The look-ahead symbol. */ int yychar; /* The semantic value of the look-ahead symbol. */ YYSTYPE yylval; /* Number of syntax errors so far. */ int yynerrs; /* Location data for the look-ahead symbol. */ YYLTYPE yylloc; int yystate; int yyn; int yyresult; /* Number of tokens to shift before error messages enabled. */ int yyerrstatus; /* Look-ahead token as an internal (translated) token number. */ int yytoken = 0; #if YYERROR_VERBOSE /* Buffer for error messages, and its allocated size. */ char yymsgbuf[128]; char *yymsg = yymsgbuf; YYSIZE_T yymsg_alloc = sizeof yymsgbuf; #endif /* Three stacks and their tools: `yyss': related to states, `yyvs': related to semantic values, `yyls': related to locations. Refer to the stacks thru separate pointers, to allow yyoverflow to reallocate them elsewhere. */ /* The state stack. */ yytype_int16 yyssa[YYINITDEPTH]; yytype_int16 *yyss = yyssa; yytype_int16 *yyssp; /* The semantic value stack. */ YYSTYPE yyvsa[YYINITDEPTH]; YYSTYPE *yyvs = yyvsa; YYSTYPE *yyvsp; /* The location stack. */ YYLTYPE yylsa[YYINITDEPTH]; YYLTYPE *yyls = yylsa; YYLTYPE *yylsp; /* The locations where the error started and ended. */ YYLTYPE yyerror_range[2]; #define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N)) YYSIZE_T yystacksize = YYINITDEPTH; /* The variables used to return semantic value and location from the action routines. */ YYSTYPE yyval; YYLTYPE yyloc; /* The number of symbols on the RHS of the reduced rule. Keep to zero when no symbol should be popped. */ int yylen = 0; YYDPRINTF ((stderr, "Starting parse\n")); yystate = 0; yyerrstatus = 0; yynerrs = 0; yychar = YYEMPTY; /* Cause a token to be read. */ /* Initialize stack pointers. Waste one element of value and location stack so that they stay on the same level as the state stack. The wasted elements are never initialized. */ yyssp = yyss; yyvsp = yyvs; yylsp = yyls; #if YYLTYPE_IS_TRIVIAL /* Initialize the default location before parsing starts. */ yylloc.first_line = yylloc.last_line = 1; yylloc.first_column = yylloc.last_column = 0; #endif goto yysetstate; /*------------------------------------------------------------. | yynewstate -- Push a new state, which is found in yystate. | `------------------------------------------------------------*/ yynewstate: /* In all cases, when you get here, the value and location stacks have just been pushed. So pushing a state here evens the stacks. */ yyssp++; yysetstate: *yyssp = yystate; if (yyss + yystacksize - 1 <= yyssp) { /* Get the current used size of the three stacks, in elements. */ YYSIZE_T yysize = yyssp - yyss + 1; #ifdef yyoverflow { /* Give user a chance to reallocate the stack. Use copies of these so that the &'s don't force the real ones into memory. */ YYSTYPE *yyvs1 = yyvs; yytype_int16 *yyss1 = yyss; YYLTYPE *yyls1 = yyls; /* Each stack pointer address is followed by the size of the data in use in that stack, in bytes. This used to be a conditional around just the two extra args, but that might be undefined if yyoverflow is a macro. */ yyoverflow (YY_("memory exhausted"), &yyss1, yysize * sizeof (*yyssp), &yyvs1, yysize * sizeof (*yyvsp), &yyls1, yysize * sizeof (*yylsp), &yystacksize); yyls = yyls1; yyss = yyss1; yyvs = yyvs1; } #else /* no yyoverflow */ # ifndef YYSTACK_RELOCATE goto yyexhaustedlab; # else /* Extend the stack our own way. */ if (YYMAXDEPTH <= yystacksize) goto yyexhaustedlab; yystacksize *= 2; if (YYMAXDEPTH < yystacksize) yystacksize = YYMAXDEPTH; { yytype_int16 *yyss1 = yyss; union yyalloc *yyptr = (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); if (! yyptr) goto yyexhaustedlab; YYSTACK_RELOCATE (yyss); YYSTACK_RELOCATE (yyvs); YYSTACK_RELOCATE (yyls); # undef YYSTACK_RELOCATE if (yyss1 != yyssa) YYSTACK_FREE (yyss1); } # endif #endif /* no yyoverflow */ yyssp = yyss + yysize - 1; yyvsp = yyvs + yysize - 1; yylsp = yyls + yysize - 1; YYDPRINTF ((stderr, "Stack size increased to %lu\n", (unsigned long int) yystacksize)); if (yyss + yystacksize - 1 <= yyssp) YYABORT; } YYDPRINTF ((stderr, "Entering state %d\n", yystate)); goto yybackup; /*-----------. | yybackup. | `-----------*/ yybackup: /* Do appropriate processing given the current state. Read a look-ahead token if we need one and don't already have one. */ /* First try to decide what to do without reference to look-ahead token. */ yyn = yypact[yystate]; if (yyn == YYPACT_NINF) goto yydefault; /* Not known => get a look-ahead token if don't already have one. */ /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ if (yychar == YYEMPTY) { YYDPRINTF ((stderr, "Reading a token: ")); yychar = YYLEX; } if (yychar <= YYEOF) { yychar = yytoken = YYEOF; YYDPRINTF ((stderr, "Now at end of input.\n")); } else { yytoken = YYTRANSLATE (yychar); YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); } /* If the proper action on seeing token YYTOKEN is to reduce or to detect an error, take that action. */ yyn += yytoken; if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) goto yydefault; yyn = yytable[yyn]; if (yyn <= 0) { if (yyn == 0 || yyn == YYTABLE_NINF) goto yyerrlab; yyn = -yyn; goto yyreduce; } if (yyn == YYFINAL) YYACCEPT; /* Count tokens shifted since error; after three, turn off error status. */ if (yyerrstatus) yyerrstatus--; /* Shift the look-ahead token. */ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); /* Discard the shifted token unless it is eof. */ if (yychar != YYEOF) yychar = YYEMPTY; yystate = yyn; *++yyvsp = yylval; *++yylsp = yylloc; goto yynewstate; /*-----------------------------------------------------------. | yydefault -- do the default action for the current state. | `-----------------------------------------------------------*/ yydefault: yyn = yydefact[yystate]; if (yyn == 0) goto yyerrlab; goto yyreduce; /*-----------------------------. | yyreduce -- Do a reduction. | `-----------------------------*/ yyreduce: /* yyn is the number of a rule to reduce with. */ yylen = yyr2[yyn]; /* If YYLEN is nonzero, implement the default value of the action: `$$ = $1'. Otherwise, the following line sets YYVAL to garbage. This behavior is undocumented and Bison users should not rely upon it. Assigning to YYVAL unconditionally makes the parser a bit smaller, and it avoids a GCC warning that YYVAL may be used uninitialized. */ yyval = yyvsp[1-yylen]; /* Default location. */ YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen); YY_REDUCE_PRINT (yyn); switch (yyn) { case 2: #line 185 "ael.y" { (yyval.pval) = parseio->pval = (yyvsp[(1) - (1)].pval); ;} break; case 3: #line 188 "ael.y" {(yyval.pval)=(yyvsp[(1) - (1)].pval);;} break; case 4: #line 189 "ael.y" { (yyval.pval) = linku1((yyvsp[(1) - (2)].pval), (yyvsp[(2) - (2)].pval)); ;} break; case 5: #line 190 "ael.y" {(yyval.pval)=(yyvsp[(1) - (2)].pval);;} break; case 6: #line 193 "ael.y" {(yyval.pval)=(yyvsp[(1) - (1)].pval);;} break; case 7: #line 194 "ael.y" {(yyval.pval)=(yyvsp[(1) - (1)].pval);;} break; case 8: #line 195 "ael.y" {(yyval.pval)=(yyvsp[(1) - (1)].pval);;} break; case 9: #line 196 "ael.y" {(yyval.pval)=0;/* allow older docs to be read */;} break; case 10: #line 199 "ael.y" { (yyval.str) = (yyvsp[(1) - (1)].str); ;} break; case 11: #line 200 "ael.y" { (yyval.str) = strdup("default"); ;} break; case 12: #line 203 "ael.y" { (yyval.pval) = npval2(PV_CONTEXT, &(yylsp[(1) - (6)]), &(yylsp[(6) - (6)])); (yyval.pval)->u1.str = (yyvsp[(3) - (6)].str); (yyval.pval)->u2.statements = (yyvsp[(5) - (6)].pval); set_dads((yyval.pval),(yyvsp[(5) - (6)].pval)); (yyval.pval)->u3.abstract = (yyvsp[(1) - (6)].intval);;} break; case 13: #line 212 "ael.y" { (yyval.intval) = 1; ;} break; case 14: #line 213 "ael.y" { (yyval.intval) = 0; ;} break; case 15: #line 214 "ael.y" { (yyval.intval) = 2; ;} break; case 16: #line 215 "ael.y" { (yyval.intval)=3; ;} break; case 17: #line 216 "ael.y" { (yyval.intval)=3; ;} break; case 18: #line 219 "ael.y" { (yyval.pval) = npval2(PV_MACRO, &(yylsp[(1) - (8)]), &(yylsp[(8) - (8)])); (yyval.pval)->u1.str = (yyvsp[(2) - (8)].str); (yyval.pval)->u2.arglist = (yyvsp[(4) - (8)].pval); (yyval.pval)->u3.macro_statements = (yyvsp[(7) - (8)].pval); set_dads((yyval.pval),(yyvsp[(7) - (8)].pval));;} break; case 19: #line 225 "ael.y" { (yyval.pval) = npval2(PV_GLOBALS, &(yylsp[(1) - (4)]), &(yylsp[(4) - (4)])); (yyval.pval)->u1.statements = (yyvsp[(3) - (4)].pval); set_dads((yyval.pval),(yyvsp[(3) - (4)].pval));;} break; case 20: #line 231 "ael.y" { (yyval.pval) = NULL; ;} break; case 21: #line 232 "ael.y" {(yyval.pval) = linku1((yyvsp[(1) - (2)].pval), (yyvsp[(2) - (2)].pval)); ;} break; case 22: #line 233 "ael.y" {(yyval.pval)=(yyvsp[(2) - (2)].pval);;} break; case 23: #line 236 "ael.y" { reset_semicount(parseio->scanner); ;} break; case 24: #line 236 "ael.y" { (yyval.pval) = npval2(PV_VARDEC, &(yylsp[(1) - (5)]), &(yylsp[(5) - (5)])); (yyval.pval)->u1.str = (yyvsp[(1) - (5)].str); (yyval.pval)->u2.val = (yyvsp[(4) - (5)].str); ;} break; case 25: #line 243 "ael.y" { (yyval.pval) = NULL; ;} break; case 26: #line 244 "ael.y" { (yyval.pval) = nword((yyvsp[(1) - (1)].str), &(yylsp[(1) - (1)])); ;} break; case 27: #line 245 "ael.y" { (yyval.pval) = linku1((yyvsp[(1) - (3)].pval), nword((yyvsp[(3) - (3)].str), &(yylsp[(3) - (3)]))); ;} break; case 28: #line 246 "ael.y" {(yyval.pval)=(yyvsp[(1) - (2)].pval);;} break; case 29: #line 249 "ael.y" {(yyval.pval)=0;;} break; case 30: #line 250 "ael.y" { (yyval.pval) = linku1((yyvsp[(1) - (2)].pval), (yyvsp[(2) - (2)].pval)); ;} break; case 31: #line 251 "ael.y" { (yyval.pval)=(yyvsp[(2) - (2)].pval);;} break; case 32: #line 254 "ael.y" {(yyval.pval)=(yyvsp[(1) - (1)].pval);;} break; case 33: #line 255 "ael.y" {(yyval.pval)=(yyvsp[(1) - (1)].pval);;} break; case 34: #line 256 "ael.y" {(yyval.pval)=(yyvsp[(1) - (1)].pval);;} break; case 35: #line 257 "ael.y" {(yyval.pval)=(yyvsp[(1) - (1)].pval);;} break; case 36: #line 258 "ael.y" {(yyval.pval)=(yyvsp[(1) - (1)].pval);;} break; case 37: #line 259 "ael.y" {(yyval.pval)=(yyvsp[(1) - (1)].pval);;} break; case 38: #line 260 "ael.y" {free((yyvsp[(1) - (2)].str)); (yyval.pval)=0;;} break; case 39: #line 261 "ael.y" {(yyval.pval)=0;/* allow older docs to be read */;} break; case 40: #line 264 "ael.y" { (yyval.pval) = npval2(PV_IGNOREPAT, &(yylsp[(1) - (4)]), &(yylsp[(4) - (4)])); (yyval.pval)->u1.str = (yyvsp[(3) - (4)].str);;} break; case 41: #line 269 "ael.y" { (yyval.pval) = npval2(PV_EXTENSION, &(yylsp[(1) - (3)]), &(yylsp[(3) - (3)])); (yyval.pval)->u1.str = (yyvsp[(1) - (3)].str); (yyval.pval)->u2.statements = (yyvsp[(3) - (3)].pval); set_dads((yyval.pval),(yyvsp[(3) - (3)].pval));;} break; case 42: #line 273 "ael.y" { (yyval.pval) = npval2(PV_EXTENSION, &(yylsp[(1) - (4)]), &(yylsp[(4) - (4)])); (yyval.pval)->u1.str = (yyvsp[(2) - (4)].str); (yyval.pval)->u2.statements = (yyvsp[(4) - (4)].pval); set_dads((yyval.pval),(yyvsp[(4) - (4)].pval)); (yyval.pval)->u4.regexten=1;;} break; case 43: #line 278 "ael.y" { (yyval.pval) = npval2(PV_EXTENSION, &(yylsp[(1) - (7)]), &(yylsp[(7) - (7)])); (yyval.pval)->u1.str = (yyvsp[(5) - (7)].str); (yyval.pval)->u2.statements = (yyvsp[(7) - (7)].pval); set_dads((yyval.pval),(yyvsp[(7) - (7)].pval)); (yyval.pval)->u3.hints = (yyvsp[(3) - (7)].str);;} break; case 44: #line 283 "ael.y" { (yyval.pval) = npval2(PV_EXTENSION, &(yylsp[(1) - (8)]), &(yylsp[(8) - (8)])); (yyval.pval)->u1.str = (yyvsp[(6) - (8)].str); (yyval.pval)->u2.statements = (yyvsp[(8) - (8)].pval); set_dads((yyval.pval),(yyvsp[(8) - (8)].pval)); (yyval.pval)->u4.regexten=1; (yyval.pval)->u3.hints = (yyvsp[(4) - (8)].str);;} break; case 45: #line 293 "ael.y" { (yyval.pval) = NULL; ;} break; case 46: #line 294 "ael.y" { (yyval.pval) = linku1((yyvsp[(1) - (2)].pval), (yyvsp[(2) - (2)].pval)); ;} break; case 47: #line 295 "ael.y" {(yyval.pval)=(yyvsp[(2) - (2)].pval);;} break; case 48: #line 301 "ael.y" { asprintf(&(yyval.str), "%s:%s:%s", (yyvsp[(1) - (5)].str), (yyvsp[(3) - (5)].str), (yyvsp[(5) - (5)].str)); free((yyvsp[(1) - (5)].str)); free((yyvsp[(3) - (5)].str)); free((yyvsp[(5) - (5)].str)); ;} break; case 49: #line 306 "ael.y" { (yyval.str) = (yyvsp[(1) - (1)].str); ;} break; case 50: #line 310 "ael.y" { (yyval.pval) = nword((yyvsp[(1) - (7)].str), &(yylsp[(1) - (7)])); (yyval.pval)->next = nword((yyvsp[(3) - (7)].str), &(yylsp[(3) - (7)])); (yyval.pval)->next->next = nword((yyvsp[(5) - (7)].str), &(yylsp[(5) - (7)])); (yyval.pval)->next->next->next = nword((yyvsp[(7) - (7)].str), &(yylsp[(7) - (7)])); ;} break; case 51: #line 318 "ael.y" { reset_parencount(parseio->scanner); ;} break; case 52: #line 318 "ael.y" { (yyval.str) = (yyvsp[(3) - (4)].str); ;} break; case 53: #line 322 "ael.y" { (yyval.pval)= npval2(PV_IF, &(yylsp[(1) - (2)]), &(yylsp[(2) - (2)])); (yyval.pval)->u1.str = (yyvsp[(2) - (2)].str); ;} break; case 54: #line 325 "ael.y" { (yyval.pval) = npval2(PV_RANDOM, &(yylsp[(1) - (2)]), &(yylsp[(2) - (2)])); (yyval.pval)->u1.str=(yyvsp[(2) - (2)].str);;} break; case 55: #line 328 "ael.y" { (yyval.pval) = npval2(PV_IFTIME, &(yylsp[(1) - (4)]), &(yylsp[(4) - (4)])); (yyval.pval)->u1.list = (yyvsp[(3) - (4)].pval); prev_word = 0; ;} break; case 56: #line 339 "ael.y" { (yyval.str) = (yyvsp[(1) - (1)].str);;} break; case 57: #line 340 "ael.y" { asprintf(&((yyval.str)), "%s%s", (yyvsp[(1) - (2)].str), (yyvsp[(2) - (2)].str)); free((yyvsp[(1) - (2)].str)); free((yyvsp[(2) - (2)].str)); prev_word = (yyval.str);;} break; case 58: #line 347 "ael.y" { (yyval.str) = (yyvsp[(1) - (1)].str); ;} break; case 59: #line 348 "ael.y" { asprintf(&((yyval.str)), "%s %s", (yyvsp[(1) - (2)].str), (yyvsp[(2) - (2)].str)); free((yyvsp[(1) - (2)].str)); free((yyvsp[(2) - (2)].str)); ;} break; case 60: #line 352 "ael.y" { asprintf(&((yyval.str)), "%s:%s", (yyvsp[(1) - (3)].str), (yyvsp[(3) - (3)].str)); free((yyvsp[(1) - (3)].str)); free((yyvsp[(3) - (3)].str)); ;} break; case 61: #line 356 "ael.y" { /* there are often '&' in hints */ asprintf(&((yyval.str)), "%s&%s", (yyvsp[(1) - (3)].str), (yyvsp[(3) - (3)].str)); free((yyvsp[(1) - (3)].str)); free((yyvsp[(3) - (3)].str));;} break; case 62: #line 362 "ael.y" { (yyval.str) = (yyvsp[(1) - (1)].str);;} break; case 63: #line 363 "ael.y" { asprintf(&((yyval.str)), "%s%s", (yyvsp[(1) - (2)].str), (yyvsp[(2) - (2)].str)); free((yyvsp[(1) - (2)].str)); free((yyvsp[(2) - (2)].str)); prev_word = (yyval.str);;} break; case 64: #line 368 "ael.y" { asprintf(&((yyval.str)), "%s%s%s", (yyvsp[(1) - (3)].str), (yyvsp[(2) - (3)].str), (yyvsp[(3) - (3)].str)); free((yyvsp[(1) - (3)].str)); free((yyvsp[(2) - (3)].str)); free((yyvsp[(3) - (3)].str)); prev_word=(yyval.str);;} break; case 65: #line 376 "ael.y" { (yyval.str) = (yyvsp[(1) - (1)].str);;} break; case 66: #line 377 "ael.y" { asprintf(&((yyval.str)), "%s%s", (yyvsp[(1) - (2)].str), (yyvsp[(2) - (2)].str)); free((yyvsp[(1) - (2)].str)); free((yyvsp[(2) - (2)].str));;} break; case 67: #line 381 "ael.y" { asprintf(&((yyval.str)), "%s:%s", (yyvsp[(1) - (3)].str), (yyvsp[(3) - (3)].str)); free((yyvsp[(1) - (3)].str)); free((yyvsp[(3) - (3)].str));;} break; case 68: #line 387 "ael.y" { (yyval.pval) = npval2(PV_SWITCH, &(yylsp[(1) - (5)]), &(yylsp[(5) - (5)])); (yyval.pval)->u1.str = (yyvsp[(2) - (5)].str); (yyval.pval)->u2.statements = (yyvsp[(4) - (5)].pval); set_dads((yyval.pval),(yyvsp[(4) - (5)].pval));;} break; case 69: #line 396 "ael.y" { (yyval.pval) = npval2(PV_STATEMENTBLOCK, &(yylsp[(1) - (3)]), &(yylsp[(3) - (3)])); (yyval.pval)->u1.list = (yyvsp[(2) - (3)].pval); set_dads((yyval.pval),(yyvsp[(2) - (3)].pval));;} break; case 70: #line 399 "ael.y" { (yyval.pval) = (yyvsp[(1) - (1)].pval); ;} break; case 71: #line 400 "ael.y" { (yyval.pval) = npval2(PV_GOTO, &(yylsp[(1) - (3)]), &(yylsp[(3) - (3)])); (yyval.pval)->u1.list = (yyvsp[(2) - (3)].pval);;} break; case 72: #line 403 "ael.y" { (yyval.pval) = npval2(PV_GOTO, &(yylsp[(1) - (3)]), &(yylsp[(3) - (3)])); (yyval.pval)->u1.list = (yyvsp[(2) - (3)].pval);;} break; case 73: #line 406 "ael.y" { (yyval.pval) = npval2(PV_LABEL, &(yylsp[(1) - (2)]), &(yylsp[(2) - (2)])); (yyval.pval)->u1.str = (yyvsp[(1) - (2)].str); ;} break; case 74: #line 409 "ael.y" {reset_semicount(parseio->scanner);;} break; case 75: #line 410 "ael.y" {reset_semicount(parseio->scanner);;} break; case 76: #line 411 "ael.y" {reset_parencount(parseio->scanner);;} break; case 77: #line 411 "ael.y" { /* XXX word_list maybe ? */ (yyval.pval) = npval2(PV_FOR, &(yylsp[(1) - (12)]), &(yylsp[(12) - (12)])); (yyval.pval)->u1.for_init = (yyvsp[(4) - (12)].str); (yyval.pval)->u2.for_test=(yyvsp[(7) - (12)].str); (yyval.pval)->u3.for_inc = (yyvsp[(10) - (12)].str); (yyval.pval)->u4.for_statements = (yyvsp[(12) - (12)].pval); set_dads((yyval.pval),(yyvsp[(12) - (12)].pval));;} break; case 78: #line 417 "ael.y" { (yyval.pval) = npval2(PV_WHILE, &(yylsp[(1) - (3)]), &(yylsp[(3) - (3)])); (yyval.pval)->u1.str = (yyvsp[(2) - (3)].str); (yyval.pval)->u2.statements = (yyvsp[(3) - (3)].pval); set_dads((yyval.pval),(yyvsp[(3) - (3)].pval));;} break; case 79: #line 421 "ael.y" { (yyval.pval) = (yyvsp[(1) - (1)].pval); ;} break; case 80: #line 422 "ael.y" { (yyval.pval) = update_last((yyvsp[(2) - (3)].pval), &(yylsp[(2) - (3)])); ;} break; case 81: #line 423 "ael.y" { (yyval.pval) = update_last((yyvsp[(1) - (2)].pval), &(yylsp[(2) - (2)])); ;} break; case 82: #line 424 "ael.y" { (yyval.pval)= npval2(PV_APPLICATION_CALL, &(yylsp[(1) - (2)]), &(yylsp[(2) - (2)])); (yyval.pval)->u1.str = (yyvsp[(1) - (2)].str);;} break; case 83: #line 427 "ael.y" {reset_semicount(parseio->scanner);;} break; case 84: #line 427 "ael.y" { char *bufx; int tot=0; pval *pptr; (yyval.pval) = npval2(PV_VARDEC, &(yylsp[(1) - (5)]), &(yylsp[(5) - (5)])); (yyval.pval)->u2.val=(yyvsp[(4) - (5)].str); /* rebuild the original string-- this is not an app call, it's an unwrapped vardec, with a func call on the LHS */ /* string to big to fit in the buffer? */ tot+=strlen((yyvsp[(1) - (5)].pval)->u1.str); for(pptr=(yyvsp[(1) - (5)].pval)->u2.arglist;pptr;pptr=pptr->next) { tot+=strlen(pptr->u1.str); tot++; /* for a sep like a comma */ } tot+=4; /* for safety */ bufx = calloc(1, tot); strcpy(bufx,(yyvsp[(1) - (5)].pval)->u1.str); strcat(bufx,"("); /* XXX need to advance the pointer or the loop is very inefficient */ for (pptr=(yyvsp[(1) - (5)].pval)->u2.arglist;pptr;pptr=pptr->next) { if ( pptr != (yyvsp[(1) - (5)].pval)->u2.arglist ) strcat(bufx,","); strcat(bufx,pptr->u1.str); } strcat(bufx,")"); #ifdef AAL_ARGCHECK if ( !ael_is_funcname((yyvsp[(1) - (5)].pval)->u1.str) ) ast_log(LOG_WARNING, "==== File: %s, Line %d, Cols: %d-%d: Function call? The name %s is not in my internal list of function names\n", my_file, (yylsp[(1) - (5)]).first_line, (yylsp[(1) - (5)]).first_column, (yylsp[(1) - (5)]).last_column, (yyvsp[(1) - (5)].pval)->u1.str); #endif (yyval.pval)->u1.str = bufx; destroy_pval((yyvsp[(1) - (5)].pval)); /* the app call it is not, get rid of that chain */ prev_word = 0; ;} break; case 85: #line 460 "ael.y" { (yyval.pval) = npval2(PV_BREAK, &(yylsp[(1) - (2)]), &(yylsp[(2) - (2)])); ;} break; case 86: #line 461 "ael.y" { (yyval.pval) = npval2(PV_RETURN, &(yylsp[(1) - (2)]), &(yylsp[(2) - (2)])); ;} break; case 87: #line 462 "ael.y" { (yyval.pval) = npval2(PV_CONTINUE, &(yylsp[(1) - (2)]), &(yylsp[(2) - (2)])); ;} break; case 88: #line 463 "ael.y" { (yyval.pval) = update_last((yyvsp[(1) - (3)].pval), &(yylsp[(2) - (3)])); (yyval.pval)->u2.statements = (yyvsp[(2) - (3)].pval); set_dads((yyval.pval),(yyvsp[(2) - (3)].pval)); (yyval.pval)->u3.else_statements = (yyvsp[(3) - (3)].pval);set_dads((yyval.pval),(yyvsp[(3) - (3)].pval));;} break; case 89: #line 467 "ael.y" { (yyval.pval)=0; ;} break; case 90: #line 470 "ael.y" { (yyval.pval) = (yyvsp[(2) - (2)].pval); ;} break; case 91: #line 471 "ael.y" { (yyval.pval) = NULL ; ;} break; case 92: #line 474 "ael.y" { (yyval.pval) = nword((yyvsp[(1) - (1)].str), &(yylsp[(1) - (1)])); ;} break; case 93: #line 475 "ael.y" { (yyval.pval) = nword((yyvsp[(1) - (3)].str), &(yylsp[(1) - (3)])); (yyval.pval)->next = nword((yyvsp[(3) - (3)].str), &(yylsp[(3) - (3)])); ;} break; case 94: #line 478 "ael.y" { (yyval.pval) = nword((yyvsp[(1) - (3)].str), &(yylsp[(1) - (3)])); (yyval.pval)->next = nword((yyvsp[(3) - (3)].str), &(yylsp[(3) - (3)])); ;} break; case 95: #line 481 "ael.y" { (yyval.pval) = nword((yyvsp[(1) - (5)].str), &(yylsp[(1) - (5)])); (yyval.pval)->next = nword((yyvsp[(3) - (5)].str), &(yylsp[(3) - (5)])); (yyval.pval)->next->next = nword((yyvsp[(5) - (5)].str), &(yylsp[(5) - (5)])); ;} break; case 96: #line 485 "ael.y" { (yyval.pval) = nword((yyvsp[(1) - (5)].str), &(yylsp[(1) - (5)])); (yyval.pval)->next = nword((yyvsp[(3) - (5)].str), &(yylsp[(3) - (5)])); (yyval.pval)->next->next = nword((yyvsp[(5) - (5)].str), &(yylsp[(5) - (5)])); ;} break; case 97: #line 489 "ael.y" { (yyval.pval) = nword(strdup("default"), &(yylsp[(1) - (5)])); (yyval.pval)->next = nword((yyvsp[(3) - (5)].str), &(yylsp[(3) - (5)])); (yyval.pval)->next->next = nword((yyvsp[(5) - (5)].str), &(yylsp[(5) - (5)])); ;} break; case 98: #line 493 "ael.y" { (yyval.pval) = nword(strdup("default"), &(yylsp[(1) - (5)])); (yyval.pval)->next = nword((yyvsp[(3) - (5)].str), &(yylsp[(3) - (5)])); (yyval.pval)->next->next = nword((yyvsp[(5) - (5)].str), &(yylsp[(5) - (5)])); ;} break; case 99: #line 499 "ael.y" { (yyval.str) = strdup("1"); ;} break; case 100: #line 500 "ael.y" { (yyval.str) = (yyvsp[(2) - (2)].str); ;} break; case 101: #line 504 "ael.y" { /* ext[, pri] default 1 */ (yyval.pval) = nword((yyvsp[(1) - (2)].str), &(yylsp[(1) - (2)])); (yyval.pval)->next = nword((yyvsp[(2) - (2)].str), &(yylsp[(2) - (2)])); ;} break; case 102: #line 507 "ael.y" { /* context, ext, pri */ (yyval.pval) = nword((yyvsp[(4) - (4)].str), &(yylsp[(4) - (4)])); (yyval.pval)->next = nword((yyvsp[(1) - (4)].str), &(yylsp[(1) - (4)])); (yyval.pval)->next->next = nword((yyvsp[(2) - (4)].str), &(yylsp[(2) - (4)])); ;} break; case 103: #line 513 "ael.y" {reset_argcount(parseio->scanner);;} break; case 104: #line 513 "ael.y" { /* XXX original code had @2 but i think we need @5 */ (yyval.pval) = npval2(PV_MACRO_CALL, &(yylsp[(1) - (5)]), &(yylsp[(5) - (5)])); (yyval.pval)->u1.str = (yyvsp[(1) - (5)].str); (yyval.pval)->u2.arglist = (yyvsp[(4) - (5)].pval);;} break; case 105: #line 518 "ael.y" { (yyval.pval)= npval2(PV_MACRO_CALL, &(yylsp[(1) - (3)]), &(yylsp[(3) - (3)])); (yyval.pval)->u1.str = (yyvsp[(1) - (3)].str); ;} break; case 106: #line 526 "ael.y" {reset_argcount(parseio->scanner);;} break; case 107: #line 526 "ael.y" { if (strcasecmp((yyvsp[(1) - (3)].str),"goto") == 0) { (yyval.pval) = npval2(PV_GOTO, &(yylsp[(1) - (3)]), &(yylsp[(2) - (3)])); free((yyvsp[(1) - (3)].str)); /* won't be using this */ ast_log(LOG_WARNING, "==== File: %s, Line %d, Cols: %d-%d: Suggestion: Use the goto statement instead of the Goto() application call in AEL.\n", my_file, (yylsp[(1) - (3)]).first_line, (yylsp[(1) - (3)]).first_column, (yylsp[(1) - (3)]).last_column ); } else { (yyval.pval)= npval2(PV_APPLICATION_CALL, &(yylsp[(1) - (3)]), &(yylsp[(2) - (3)])); (yyval.pval)->u1.str = (yyvsp[(1) - (3)].str); } ;} break; case 108: #line 537 "ael.y" { (yyval.pval) = update_last((yyvsp[(1) - (3)].pval), &(yylsp[(3) - (3)])); if( (yyval.pval)->type == PV_GOTO ) (yyval.pval)->u1.list = (yyvsp[(2) - (3)].pval); else (yyval.pval)->u2.arglist = (yyvsp[(2) - (3)].pval); ;} break; case 109: #line 544 "ael.y" { (yyval.pval) = update_last((yyvsp[(1) - (2)].pval), &(yylsp[(2) - (2)])); ;} break; case 110: #line 547 "ael.y" { (yyval.str) = (yyvsp[(1) - (1)].str) ;} break; case 111: #line 548 "ael.y" { (yyval.str) = strdup(""); ;} break; case 112: #line 551 "ael.y" { (yyval.pval) = nword((yyvsp[(1) - (1)].str), &(yylsp[(1) - (1)])); ;} break; case 113: #line 552 "ael.y" { (yyval.pval)= npval(PV_WORD,0/*@1.first_line*/,0/*@1.last_line*/,0/* @1.first_column*/, 0/*@1.last_column*/); (yyval.pval)->u1.str = strdup(""); ;} break; case 114: #line 555 "ael.y" { (yyval.pval) = linku1((yyvsp[(1) - (3)].pval), nword((yyvsp[(3) - (3)].str), &(yylsp[(3) - (3)]))); ;} break; case 115: #line 558 "ael.y" { (yyval.pval) = NULL; ;} break; case 116: #line 559 "ael.y" { (yyval.pval) = linku1((yyvsp[(1) - (2)].pval), (yyvsp[(2) - (2)].pval)); ;} break; case 117: #line 562 "ael.y" { (yyval.pval) = npval2(PV_CASE, &(yylsp[(1) - (4)]), &(yylsp[(3) - (4)])); /* XXX 3 or 4 ? */ (yyval.pval)->u1.str = (yyvsp[(2) - (4)].str); (yyval.pval)->u2.statements = (yyvsp[(4) - (4)].pval); set_dads((yyval.pval),(yyvsp[(4) - (4)].pval));;} break; case 118: #line 566 "ael.y" { (yyval.pval) = npval2(PV_DEFAULT, &(yylsp[(1) - (3)]), &(yylsp[(3) - (3)])); (yyval.pval)->u1.str = NULL; (yyval.pval)->u2.statements = (yyvsp[(3) - (3)].pval);set_dads((yyval.pval),(yyvsp[(3) - (3)].pval));;} break; case 119: #line 570 "ael.y" { (yyval.pval) = npval2(PV_PATTERN, &(yylsp[(1) - (4)]), &(yylsp[(4) - (4)])); /* XXX@3 or @4 ? */ (yyval.pval)->u1.str = (yyvsp[(2) - (4)].str); (yyval.pval)->u2.statements = (yyvsp[(4) - (4)].pval);set_dads((yyval.pval),(yyvsp[(4) - (4)].pval));;} break; case 120: #line 576 "ael.y" { (yyval.pval) = NULL; ;} break; case 121: #line 577 "ael.y" { (yyval.pval) = linku1((yyvsp[(1) - (2)].pval), (yyvsp[(2) - (2)].pval)); ;} break; case 122: #line 580 "ael.y" {(yyval.pval)=(yyvsp[(1) - (1)].pval);;} break; case 123: #line 581 "ael.y" { (yyval.pval)=(yyvsp[(1) - (1)].pval);;} break; case 124: #line 582 "ael.y" { (yyval.pval) = npval2(PV_CATCH, &(yylsp[(1) - (5)]), &(yylsp[(5) - (5)])); (yyval.pval)->u1.str = (yyvsp[(2) - (5)].str); (yyval.pval)->u2.statements = (yyvsp[(4) - (5)].pval); set_dads((yyval.pval),(yyvsp[(4) - (5)].pval));;} break; case 125: #line 588 "ael.y" { (yyval.pval) = npval2(PV_SWITCHES, &(yylsp[(1) - (4)]), &(yylsp[(2) - (4)])); (yyval.pval)->u1.list = (yyvsp[(3) - (4)].pval); set_dads((yyval.pval),(yyvsp[(3) - (4)].pval));;} break; case 126: #line 593 "ael.y" { (yyval.pval) = npval2(PV_ESWITCHES, &(yylsp[(1) - (4)]), &(yylsp[(2) - (4)])); (yyval.pval)->u1.list = (yyvsp[(3) - (4)].pval); set_dads((yyval.pval),(yyvsp[(3) - (4)].pval));;} break; case 127: #line 598 "ael.y" { (yyval.pval) = NULL; ;} break; case 128: #line 599 "ael.y" { (yyval.pval) = linku1(nword((yyvsp[(1) - (3)].str), &(yylsp[(1) - (3)])), (yyvsp[(3) - (3)].pval)); ;} break; case 129: #line 600 "ael.y" { char *x; asprintf(&x,"%s@%s", (yyvsp[(1) - (5)].str),(yyvsp[(3) - (5)].str)); free((yyvsp[(1) - (5)].str)); free((yyvsp[(3) - (5)].str)); (yyval.pval) = linku1(nword(x, &(yylsp[(1) - (5)])), (yyvsp[(5) - (5)].pval));;} break; case 130: #line 602 "ael.y" {(yyval.pval)=(yyvsp[(2) - (2)].pval);;} break; case 131: #line 605 "ael.y" { (yyval.pval) = nword((yyvsp[(1) - (1)].str), &(yylsp[(1) - (1)])); ;} break; case 132: #line 606 "ael.y" { (yyval.pval) = nword((yyvsp[(1) - (3)].str), &(yylsp[(1) - (3)])); (yyval.pval)->u2.arglist = (yyvsp[(3) - (3)].pval); prev_word=0; /* XXX sure ? */ ;} break; case 133: #line 613 "ael.y" { (yyval.pval) = (yyvsp[(1) - (2)].pval); ;} break; case 134: #line 614 "ael.y" { (yyval.pval) = linku1((yyvsp[(1) - (3)].pval), (yyvsp[(2) - (3)].pval)); ;} break; case 135: #line 615 "ael.y" {(yyval.pval)=(yyvsp[(1) - (2)].pval);;} break; case 136: #line 618 "ael.y" { (yyval.pval) = npval2(PV_INCLUDES, &(yylsp[(1) - (4)]), &(yylsp[(4) - (4)])); (yyval.pval)->u1.list = (yyvsp[(3) - (4)].pval);set_dads((yyval.pval),(yyvsp[(3) - (4)].pval));;} break; case 137: #line 621 "ael.y" { (yyval.pval) = npval2(PV_INCLUDES, &(yylsp[(1) - (3)]), &(yylsp[(3) - (3)]));;} break; /* Line 1270 of yacc.c. */ #line 2956 "ael.tab.c" default: break; } YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); YYPOPSTACK (yylen); yylen = 0; YY_STACK_PRINT (yyss, yyssp); *++yyvsp = yyval; *++yylsp = yyloc; /* Now `shift' the result of the reduction. Determine what state that goes to, based on the state we popped back to and the rule number reduced by. */ yyn = yyr1[yyn]; yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) yystate = yytable[yystate]; else yystate = yydefgoto[yyn - YYNTOKENS]; goto yynewstate; /*------------------------------------. | yyerrlab -- here on detecting error | `------------------------------------*/ yyerrlab: /* If not already recovering from an error, report this error. */ if (!yyerrstatus) { ++yynerrs; #if ! YYERROR_VERBOSE yyerror (&yylloc, parseio, YY_("syntax error")); #else { YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) { YYSIZE_T yyalloc = 2 * yysize; if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) yyalloc = YYSTACK_ALLOC_MAXIMUM; if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); yymsg = (char *) YYSTACK_ALLOC (yyalloc); if (yymsg) yymsg_alloc = yyalloc; else { yymsg = yymsgbuf; yymsg_alloc = sizeof yymsgbuf; } } if (0 < yysize && yysize <= yymsg_alloc) { (void) yysyntax_error (yymsg, yystate, yychar); yyerror (&yylloc, parseio, yymsg); } else { yyerror (&yylloc, parseio, YY_("syntax error")); if (yysize != 0) goto yyexhaustedlab; } } #endif } yyerror_range[0] = yylloc; if (yyerrstatus == 3) { /* If just tried and failed to reuse look-ahead token after an error, discard it. */ if (yychar <= YYEOF) { /* Return failure if at end of input. */ if (yychar == YYEOF) YYABORT; } else { yydestruct ("Error: discarding", yytoken, &yylval, &yylloc, parseio); yychar = YYEMPTY; } } /* Else will try to reuse look-ahead token after shifting the error token. */ goto yyerrlab1; /*---------------------------------------------------. | yyerrorlab -- error raised explicitly by YYERROR. | `---------------------------------------------------*/ yyerrorlab: /* Pacify compilers like GCC when the user code never invokes YYERROR and the label yyerrorlab therefore never appears in user code. */ if (/*CONSTCOND*/ 0) goto yyerrorlab; yyerror_range[0] = yylsp[1-yylen]; /* Do not reclaim the symbols of the rule which action triggered this YYERROR. */ YYPOPSTACK (yylen); yylen = 0; YY_STACK_PRINT (yyss, yyssp); yystate = *yyssp; goto yyerrlab1; /*-------------------------------------------------------------. | yyerrlab1 -- common code for both syntax error and YYERROR. | `-------------------------------------------------------------*/ yyerrlab1: yyerrstatus = 3; /* Each real token shifted decrements this. */ for (;;) { yyn = yypact[yystate]; if (yyn != YYPACT_NINF) { yyn += YYTERROR; if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) { yyn = yytable[yyn]; if (0 < yyn) break; } } /* Pop the current state because it cannot handle the error token. */ if (yyssp == yyss) YYABORT; yyerror_range[0] = *yylsp; yydestruct ("Error: popping", yystos[yystate], yyvsp, yylsp, parseio); YYPOPSTACK (1); yystate = *yyssp; YY_STACK_PRINT (yyss, yyssp); } if (yyn == YYFINAL) YYACCEPT; *++yyvsp = yylval; yyerror_range[1] = yylloc; /* Using YYLLOC is tempting, but would change the location of the look-ahead. YYLOC is available though. */ YYLLOC_DEFAULT (yyloc, (yyerror_range - 1), 2); *++yylsp = yyloc; /* Shift the error token. */ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); yystate = yyn; goto yynewstate; /*-------------------------------------. | yyacceptlab -- YYACCEPT comes here. | `-------------------------------------*/ yyacceptlab: yyresult = 0; goto yyreturn; /*-----------------------------------. | yyabortlab -- YYABORT comes here. | `-----------------------------------*/ yyabortlab: yyresult = 1; goto yyreturn; #ifndef yyoverflow /*-------------------------------------------------. | yyexhaustedlab -- memory exhaustion comes here. | `-------------------------------------------------*/ yyexhaustedlab: yyerror (&yylloc, parseio, YY_("memory exhausted")); yyresult = 2; /* Fall through. */ #endif yyreturn: if (yychar != YYEOF && yychar != YYEMPTY) yydestruct ("Cleanup: discarding lookahead", yytoken, &yylval, &yylloc, parseio); /* Do not reclaim the symbols of the rule which action triggered this YYABORT or YYACCEPT. */ YYPOPSTACK (yylen); YY_STACK_PRINT (yyss, yyssp); while (yyssp != yyss) { yydestruct ("Cleanup: popping", yystos[*yyssp], yyvsp, yylsp, parseio); YYPOPSTACK (1); } #ifndef yyoverflow if (yyss != yyssa) YYSTACK_FREE (yyss); #endif #if YYERROR_VERBOSE if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); #endif return yyresult; } #line 626 "ael.y" static char *token_equivs1[] = { "AMPER", "AT", "BAR", "COLON", "COMMA", "EQ", "EXTENMARK", "KW_BREAK", "KW_CASE", "KW_CATCH", "KW_CONTEXT", "KW_CONTINUE", "KW_DEFAULT", "KW_ELSE", "KW_ESWITCHES", "KW_FOR", "KW_GLOBALS", "KW_GOTO", "KW_HINT", "KW_IFTIME", "KW_IF", "KW_IGNOREPAT", "KW_INCLUDES" "KW_JUMP", "KW_MACRO", "KW_PATTERN", "KW_REGEXTEN", "KW_RETURN", "KW_SWITCHES", "KW_SWITCH", "KW_WHILE", "LC", "LP", "RC", "RP", "SEMI", }; static char *token_equivs2[] = { "&", "@", "|", ":", ",", "=", "=>", "break", "case", "catch", "context", "continue", "default", "else", "eswitches", "for", "globals", "goto", "hint", "ifTime", "if", "ignorepat", "includes" "jump", "macro", "pattern", "regexten", "return", "switches", "switch", "while", "{", "(", "}", ")", ";", }; static char *ael_token_subst(const char *mess) { /* calc a length, malloc, fill, and return; yyerror had better free it! */ int len=0,i; const char *p; char *res, *s,*t; int token_equivs_entries = sizeof(token_equivs1)/sizeof(char*); for (p=mess; *p; p++) { for (i=0; ifirst_line == locp->last_line) { ast_log(LOG_ERROR, "==== File: %s, Line %d, Cols: %d-%d: Error: %s\n", my_file, locp->first_line, locp->first_column, locp->last_column, s2); } else { ast_log(LOG_ERROR, "==== File: %s, Line %d Col %d to Line %d Col %d: Error: %s\n", my_file, locp->first_line, locp->first_column, locp->last_line, locp->last_column, s2); } free(s2); parseio->syntax_error_count++; } static struct pval *npval(pvaltype type, int first_line, int last_line, int first_column, int last_column) { pval *z = calloc(1, sizeof(struct pval)); z->type = type; z->startline = first_line; z->endline = last_line; z->startcol = first_column; z->endcol = last_column; z->filename = strdup(my_file); return z; } static struct pval *npval2(pvaltype type, YYLTYPE *first, YYLTYPE *last) { return npval(type, first->first_line, last->last_line, first->first_column, last->last_column); } static struct pval *update_last(pval *obj, YYLTYPE *last) { obj->endline = last->last_line; obj->endcol = last->last_column; return obj; } /* frontend for npval to create a PV_WORD string from the given token */ static pval *nword(char *string, YYLTYPE *pos) { pval *p = npval2(PV_WORD, pos, pos); if (p) p->u1.str = string; return p; } /* append second element to the list in the first one */ static pval * linku1(pval *head, pval *tail) { if (!head) return tail; if (tail) { if (!head->next) { head->next = tail; } else { head->u1_last->next = tail; } head->u1_last = tail; tail->prev = head; /* the dad link only points to containers */ } return head; } /* this routine adds a dad ptr to each element in the list */ static void set_dads(struct pval *dad, struct pval *child_list) { struct pval *t; for(t=child_list;t;t=t->next) /* simple stuff */ t->dad = dad; } asterisk-1.4.21.2/README0000644000175000017500000002417710622771241013723 0ustar maniacmaniacThe Asterisk(R) Open Source PBX by Mark Spencer and the Asterisk.org developer community Copyright (C) 2001-2006 Digium, Inc. and other copyright holders. ================================================================ * SECURITY It is imperative that you read and fully understand the contents of the security information file (doc/security.txt) before you attempt to configure and run an Asterisk server. * WHAT IS ASTERISK ? Asterisk is an Open Source PBX and telephony toolkit. It is, in a sense, middleware between Internet and telephony channels on the bottom, and Internet and telephony applications at the top. For more information on the project itself, please visit the Asterisk home page at: http://www.asterisk.org In addition you'll find lots of information compiled by the Asterisk community on this Wiki: http://www.voip-info.org/wiki-Asterisk There is a book on Asterisk published by O'Reilly under the Creative Commons License. It is available in book stores as well as in a downloadable version on the http://www.asteriskdocs.org web site. * SUPPORTED OPERATING SYSTEMS == Linux == The Asterisk Open Source PBX is developed and tested primarily on the GNU/Linux operating system, and is supported on every major GNU/Linux distribution. == Others == Asterisk has also been 'ported' and reportedly runs properly on other operating systems as well, including Sun Solaris, Apple's Mac OS X, and the BSD variants. * GETTING STARTED First, be sure you've got supported hardware (but note that you don't need ANY special hardware, not even a soundcard) to install and run Asterisk. Supported telephony hardware includes: * All Wildcard (tm) products from Digium (www.digium.com) * QuickNet Internet PhoneJack and LineJack (http://www.quicknet.net) * any full duplex sound card supported by ALSA or OSS * any ISDN card supported by mISDN on Linux (BRI) * The Xorcom AstriBank channel bank * VoiceTronix OpenLine products The are several drivers for ISDN BRI cards available from third party sources. Check the voip-info.org wiki for more information on chan_capi and zaphfc. * UPGRADING FROM AN EARLIER VERSION If you are updating from a previous version of Asterisk, make sure you read the UPGRADE.txt file in the source directory. There are some files and configuration options that you will have to change, even though we made every effort possible to maintain backwards compatibility. In order to discover new features to use, please check the configuration examples in the /configs directory of the source code distribution. To discover the major new features of Asterisk 1.2, please visit http://edvina.net/asterisk1-2/ * NEW INSTALLATIONS Ensure that your system contains a compatible compiler and development libraries. Asterisk requires either the GNU Compiler Collection (GCC) version 3.0 or higher, or a compiler that supports the C99 specification and some of the gcc language extensions. In addition, your system needs to have the C library headers available, and the headers and libraries for OpenSSL, ncurses and zlib. On many distributions, these files are installed by packages with names like 'glibc-devel', 'ncurses-devel', 'openssl-devel' and 'zlib-devel' or similar. So let's proceed: 1) Read this README file. There are more documents than this one in the doc/ directory. You may also want to check the configuration files that contain examples and reference guides. They are all in the configs/ directory. 2) Run "./configure" Execute the configure script to guess values for system-dependent variables used during compilation. 3) Run "make menuselect" [optional] This is needed if you want to select the modules that will be compiled and to check modules dependencies. 4) Run "make" Assuming the build completes successfully: 5) Run "make install" Each time you update or checkout from the repository, you are strongly encouraged to ensure all previous object files are removed to avoid internal inconsistency in Asterisk. Normally, this is automatically done with the presence of the file .cleancount, which increments each time a 'make clean' is required, and the file .lastclean, which contains the last .cleancount used. If this is your first time working with Asterisk, you may wish to install the sample PBX, with demonstration extensions, etc. If so, run: 6) "make samples" Doing so will overwrite any existing config files you have. Finally, you can launch Asterisk in the foreground mode (not a daemon) with: # asterisk -vvvc You'll see a bunch of verbose messages fly by your screen as Asterisk initializes (that's the "very very verbose" mode). When it's ready, if you specified the "c" then you'll get a command line console, that looks like this: *CLI> You can type "help" at any time to get help with the system. For help with a specific command, type "help ". To start the PBX using your sound card, you can type "dial" to dial the PBX. Then you can use "answer", "hangup", and "dial" to simulate the actions of a telephone. Remember that if you don't have a full duplex sound card (and Asterisk will tell you somewhere in its verbose messages if you do/don't) then it won't work right (not yet). "man asterisk" at the Unix/Linux command prompt will give you detailed information on how to start and stop Asterisk, as well as all the command line options for starting Asterisk. Feel free to look over the configuration files in /etc/asterisk, where you'll find a lot of information about what you can do with Asterisk. * ABOUT CONFIGURATION FILES All Asterisk configuration files share a common format. Comments are delimited by ';' (since '#' of course, being a DTMF digit, may occur in many places). A configuration file is divided into sections whose names appear in []'s. Each section typically contains two types of statements, those of the form 'variable = value', and those of the form 'object => parameters'. Internally the use of '=' and '=>' is exactly the same, so they're used only to help make the configuration file easier to understand, and do not affect how it is actually parsed. Entries of the form 'variable=value' set the value of some parameter in asterisk. For example, in zapata.conf, one might specify: switchtype=national in order to indicate to Asterisk that the switch they are connecting to is of the type "national". In general, the parameter will apply to instantiations which occur below its specification. For example, if the configuration file read: switchtype = national channel => 1-4 channel => 10-12 switchtype = dms100 channel => 25-47 the "national" switchtype would be applied to channels one through four and channels 10 through 12, whereas the "dms100" switchtype would apply to channels 25 through 47. The "object => parameters" instantiates an object with the given parameters. For example, the line "channel => 25-47" creates objects for the channels 25 through 47 of the card, obtaining the settings from the variables specified above. * SPECIAL NOTE ON TIME Those using SIP phones should be aware that Asterisk is sensitive to large jumps in time. Manually changing the system time using date(1) (or other similar commands) may cause SIP registrations and other internal processes to fail. If your system cannot keep accurate time by itself use NTP (http://www.ntp.org/) to keep the system clock synchronized to "real time". NTP is designed to keep the system clock synchronized by speeding up or slowing down the system clock until it is synchronized to "real time" rather than by jumping the time and causing discontinuities. Most Linux distributions include precompiled versions of NTP. Beware of some time synchronization methods that get the correct real time periodically and then manually set the system clock. Apparent time changes due to daylight savings time are just that, apparent. The use of daylight savings time in a Linux system is purely a user interface issue and does not affect the operation of the Linux kernel or Asterisk. The system clock on Linux kernels operates on UTC. UTC does not use daylight savings time. Also note that this issue is separate from the clocking of TDM channels, and is known to at least affect SIP registrations. * FILE DESCRIPTORS Depending on the size of your system and your configuration, Asterisk can consume a large number of file descriptors. In UNIX, file descriptors are used for more than just files on disk. File descriptors are also used for handling network communication (e.g. SIP, IAX2, or H.323 calls) and hardware access (e.g. analog and digital trunk hardware). Asterisk accesses many on-disk files for everything from configuration information to voicemail storage. Most systems limit the number of file descriptors that Asterisk can have open at one time. This can limit the number of simultaneous calls that your system can handle. For example, if the limit is set at 1024 (a common default value) Asterisk can handle approxiately 150 SIP calls simultaneously. To change the number of file descriptors follow the instructions for your system below: == PAM-based Linux System == If your system uses PAM (Pluggable Authentication Modules) edit /etc/security/limits.conf. Add these lines to the bottom of the file: root soft nofile 4096 root hard nofile 8196 asterisk soft nofile 4096 asterisk hard nofile 8196 (adjust the numbers to taste). You may need to reboot the system for these changes to take effect. == Generic UNIX System == If there are no instructions specifically adapted to your system above you can try adding the command "ulimit -n 8192" to the script that starts Asterisk. * MORE INFORMATION See the doc directory for more documentation on various features. Again, please read all the configuration samples that include documentation on the configuration options. Finally, you may wish to visit the web site and join the mailing list if you're interested in getting more information. http://www.asterisk.org/support Welcome to the growing worldwide community of Asterisk users! Mark Spencer ---- Asterisk is a trademark belonging to Digium, inc asterisk-1.4.21.2/mkinstalldirs0000755000175000017500000000132410427665642015650 0ustar maniacmaniac#! /bin/sh # mkinstalldirs --- make directory hierarchy # Author: Noah Friedman # Created: 1993-05-16 # Public domain # $Id: mkinstalldirs 25628 2006-05-08 16:02:42Z kpfleming $ errstatus=0 for file do set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'` shift pathcomp= for d do pathcomp="$pathcomp$d" case "$pathcomp" in -* ) pathcomp=./$pathcomp ;; esac if test ! -d "$pathcomp"; then echo "mkdir $pathcomp" mkdir "$pathcomp" || lasterr=$? if test ! -d "$pathcomp"; then errstatus=$lasterr fi fi pathcomp="$pathcomp/" done done exit $errstatus # mkinstalldirs ends here asterisk-1.4.21.2/agi/0000755000175000017500000000000011041455742013571 5ustar maniacmaniacasterisk-1.4.21.2/agi/Makefile0000644000175000017500000000162010711455300015221 0ustar maniacmaniac# # Asterisk -- A telephony toolkit for Linux. # # Makefile for AGI-related stuff # # Copyright (C) 1999-2006, Digium # # Mark Spencer # # This program is free software, distributed under the terms of # the GNU General Public License # .PHONY: clean all uninstall AGIS=agi-test.agi eagi-test eagi-sphinx-test jukebox.agi ifeq ($(OSARCH),SunOS) LIBS+=-lsocket -lnsl endif include $(ASTTOPDIR)/Makefile.rules all: $(AGIS) strcompat.c: ../main/strcompat.c @cp $< $@ eagi-test: eagi-test.o strcompat.o eagi-sphinx-test: eagi-sphinx-test.o install: all mkdir -p $(DESTDIR)$(AGI_DIR) for x in $(AGIS); do $(INSTALL) -m 755 $$x $(DESTDIR)$(AGI_DIR) ; done uninstall: for x in $(AGIS); do rm -f $(DESTDIR)$(AGI_DIR)/$$x ; done clean: rm -f *.so *.o look eagi-test eagi-sphinx-test rm -f .*.o.d .*.oo.d *.s *.i rm -f strcompat.c ifneq ($(wildcard .*.d),) include .*.d endif asterisk-1.4.21.2/agi/fastagi-test0000644000175000017500000000426710374426007016120 0ustar maniacmaniac#!/usr/bin/perl use strict; use Socket; use Carp; use IO::Handle; my $port = 4573; $|=1; # Setup some variables my %AGI; my $tests = 0; my $fail = 0; my $pass = 0; sub checkresult { my ($res) = @_; my $retval; $tests++; chomp $res; if ($res =~ /^200/) { $res =~ /result=(-?\d+)/; if (!length($1)) { print STDERR "FAIL ($res)\n"; $fail++; } else { print STDERR "PASS ($1)\n"; $pass++; } } else { print STDERR "FAIL (unexpected result '$res')\n"; $fail++; } } socket(SERVER, PF_INET, SOCK_STREAM, 0); setsockopt(SERVER, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)); bind(SERVER, sockaddr_in($port, INADDR_ANY)) || die("can't bind\n"); listen(SERVER, SOMAXCONN); for(;;) { my $raddr = accept(CLIENT, SERVER); my ($s, $p) = sockaddr_in($raddr); CLIENT->autoflush(1); while() { chomp; last unless length($_); if (/^agi_(\w+)\:\s+(.*)$/) { $AGI{$1} = $2; } } print STDERR "AGI Environment Dump from $s:$p --\n"; foreach my $i (sort keys %AGI) { print STDERR " -- $i = $AGI{$i}\n"; } print STDERR "1. Testing 'sendfile'..."; print CLIENT "STREAM FILE beep \"\"\n"; my $result = ; &checkresult($result); print STDERR "2. Testing 'sendtext'..."; print CLIENT "SEND TEXT \"hello world\"\n"; my $result = ; &checkresult($result); print STDERR "3. Testing 'sendimage'..."; print CLIENT "SEND IMAGE asterisk-image\n"; my $result = ; &checkresult($result); print STDERR "4. Testing 'saynumber'..."; print CLIENT "SAY NUMBER 192837465 \"\"\n"; my $result = ; &checkresult($result); print STDERR "5. Testing 'waitdtmf'..."; print CLIENT "WAIT FOR DIGIT 1000\n"; my $result = ; &checkresult($result); print STDERR "6. Testing 'record'..."; print CLIENT "RECORD FILE testagi gsm 1234 3000\n"; my $result = ; &checkresult($result); print STDERR "6a. Testing 'record' playback..."; print CLIENT "STREAM FILE testagi \"\"\n"; my $result = ; &checkresult($result); close(CLIENT); print STDERR "================== Complete ======================\n"; print STDERR "$tests tests completed, $pass passed, $fail failed\n"; print STDERR "==================================================\n"; } asterisk-1.4.21.2/agi/numeralize0000644000175000017500000000172210374426007015671 0ustar maniacmaniac#!/usr/bin/perl # # Build a database linking filenames to their numerical representations # using a keypad for the DialAnMp3 application # $mp3dir="/usr/media/mpeg3"; dbmopen(%DIGITS, "/var/lib/asterisk/mp3list", 0644) || die("Unable to open mp3list");; sub process_dir { my ($dir) = @_; my $file; my $digits; my @entries; opendir(DIR, $dir); @entries = readdir(DIR); closedir(DIR); foreach $_ (@entries) { if (!/^\./) { $file = "$dir/$_"; if (-d "$file") { process_dir("$file"); } else { $digits = $_; $digits =~ s/[^ \w]+//g; $digits =~ s/\_/ /g; $digits =~ tr/[a-z]/[A-Z]/; $digits =~ tr/[A-C]/2/; $digits =~ tr/[D-F]/3/; $digits =~ tr/[G-I]/4/; $digits =~ tr/[J-L]/5/; $digits =~ tr/[M-O]/6/; $digits =~ tr/[P-S]/7/; $digits =~ tr/[T-V]/8/; $digits =~ tr/[W-Z]/9/; $digits =~ s/\s+/ /; print "File: $file, digits: $digits\n"; $DIGITS{$file} = $digits; } } } } process_dir($mp3dir); asterisk-1.4.21.2/agi/eagi-test.c0000644000175000017500000000713710423203321015612 0ustar maniacmaniac/* * Extended AGI test application * * This code is released into the public domain * with no warranty of any kind */ #include #include #include #include #include #include #include "asterisk.h" #include "asterisk/compat.h" #define AUDIO_FILENO (STDERR_FILENO + 1) static int read_environment(void) { char buf[256]; char *val; /* Read environment */ for(;;) { fgets(buf, sizeof(buf), stdin); if (feof(stdin)) return -1; buf[strlen(buf) - 1] = '\0'; /* Check for end of environment */ if (!strlen(buf)) return 0; val = strchr(buf, ':'); if (!val) { fprintf(stderr, "Invalid environment: '%s'\n", buf); return -1; } *val = '\0'; val++; val++; /* Skip space */ fprintf(stderr, "Environment: '%s' is '%s'\n", buf, val); /* Load into normal environment */ setenv(buf, val, 1); } /* Never reached */ return 0; } static char *wait_result(void) { fd_set fds; int res; int bytes = 0; static char astresp[256]; char audiobuf[4096]; for (;;) { FD_ZERO(&fds); FD_SET(STDIN_FILENO, &fds); FD_SET(AUDIO_FILENO, &fds); /* Wait for *some* sort of I/O */ res = select(AUDIO_FILENO + 1, &fds, NULL, NULL, NULL); if (res < 0) { fprintf(stderr, "Error in select: %s\n", strerror(errno)); return NULL; } if (FD_ISSET(STDIN_FILENO, &fds)) { fgets(astresp, sizeof(astresp), stdin); if (feof(stdin)) { fprintf(stderr, "Got hungup on apparently\n"); return NULL; } astresp[strlen(astresp) - 1] = '\0'; fprintf(stderr, "Ooh, got a response from Asterisk: '%s'\n", astresp); return astresp; } if (FD_ISSET(AUDIO_FILENO, &fds)) { res = read(AUDIO_FILENO, audiobuf, sizeof(audiobuf)); if (res > 0) { /* XXX Process the audio with sphinx here XXX */ #if 0 fprintf(stderr, "Got %d/%d bytes of audio\n", res, bytes); #endif bytes += res; /* Prentend we detected some audio after 3 seconds */ if (bytes > 16000 * 3) { return "Sample Message"; bytes = 0; } } } } } static char *run_command(char *command) { fprintf(stdout, "%s\n", command); return wait_result(); } static int run_script(void) { char *res; res = run_command("STREAM FILE demo-enterkeywords 0123456789*#"); if (!res) { fprintf(stderr, "Failed to execute command\n"); return -1; } fprintf(stderr, "1. Result is '%s'\n", res); res = run_command("STREAM FILE demo-nomatch 0123456789*#"); if (!res) { fprintf(stderr, "Failed to execute command\n"); return -1; } fprintf(stderr, "2. Result is '%s'\n", res); res = run_command("SAY NUMBER 23452345 0123456789*#"); if (!res) { fprintf(stderr, "Failed to execute command\n"); return -1; } fprintf(stderr, "3. Result is '%s'\n", res); res = run_command("GET DATA demo-enterkeywords"); if (!res) { fprintf(stderr, "Failed to execute command\n"); return -1; } fprintf(stderr, "4. Result is '%s'\n", res); res = run_command("STREAM FILE auth-thankyou \"\""); if (!res) { fprintf(stderr, "Failed to execute command\n"); return -1; } fprintf(stderr, "5. Result is '%s'\n", res); return 0; } int main(int argc, char *argv[]) { char *tmp; int ver = 0; int subver = 0; /* Setup stdin/stdout for line buffering */ setlinebuf(stdin); setlinebuf(stdout); if (read_environment()) { fprintf(stderr, "Failed to read environment: %s\n", strerror(errno)); exit(1); } tmp = getenv("agi_enhanced"); if (tmp) { if (sscanf(tmp, "%d.%d", &ver, &subver) != 2) ver = 0; } if (ver < 1) { fprintf(stderr, "No enhanced AGI services available. Use EAGI, not AGI\n"); exit(1); } if (run_script()) return -1; exit(0); } asterisk-1.4.21.2/agi/eagi-sphinx-test.c0000644000175000017500000001210210423203321017105 0ustar maniacmaniac/* * Extended AGI test application * * This code is released into public domain * without any warranty of any kind. * */ #include #include #include #include #include #include #include #include #include #include #include #include "asterisk.h" #include "asterisk/compat.h" #define AUDIO_FILENO (STDERR_FILENO + 1) #define SPHINX_HOST "192.168.1.108" #define SPHINX_PORT 3460 static int sphinx_sock = -1; static int connect_sphinx(void) { struct hostent *hp; struct sockaddr_in sin; int res; hp = gethostbyname(SPHINX_HOST); if (!hp) { fprintf(stderr, "Unable to resolve '%s'\n", SPHINX_HOST); return -1; } sphinx_sock = socket(PF_INET, SOCK_STREAM, 0); if (sphinx_sock < 0) { fprintf(stderr, "Unable to allocate socket: %s\n", strerror(errno)); return -1; } memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(SPHINX_PORT); memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr)); if (connect(sphinx_sock, (struct sockaddr *)&sin, sizeof(sin))) { fprintf(stderr, "Unable to connect on socket: %s\n", strerror(errno)); close(sphinx_sock); sphinx_sock = -1; return -1; } res = fcntl(sphinx_sock, F_GETFL); if ((res < 0) || (fcntl(sphinx_sock, F_SETFL, res | O_NONBLOCK) < 0)) { fprintf(stderr, "Unable to set flags on socket: %s\n", strerror(errno)); close(sphinx_sock); sphinx_sock = -1; return -1; } return 0; } static int read_environment(void) { char buf[256]; char *val; /* Read environment */ for(;;) { fgets(buf, sizeof(buf), stdin); if (feof(stdin)) return -1; buf[strlen(buf) - 1] = '\0'; /* Check for end of environment */ if (!strlen(buf)) return 0; val = strchr(buf, ':'); if (!val) { fprintf(stderr, "Invalid environment: '%s'\n", buf); return -1; } *val = '\0'; val++; val++; /* Skip space */ fprintf(stderr, "Environment: '%s' is '%s'\n", buf, val); /* Load into normal environment */ setenv(buf, val, 1); } /* Never reached */ return 0; } static char *wait_result(void) { fd_set fds; int res; int max; static char astresp[256]; static char sphinxresp[256]; char audiobuf[4096]; for (;;) { FD_ZERO(&fds); FD_SET(STDIN_FILENO, &fds); FD_SET(AUDIO_FILENO, &fds); max = AUDIO_FILENO; if (sphinx_sock > -1) { FD_SET(sphinx_sock, &fds); if (sphinx_sock > max) max = sphinx_sock; } /* Wait for *some* sort of I/O */ res = select(max + 1, &fds, NULL, NULL, NULL); if (res < 0) { fprintf(stderr, "Error in select: %s\n", strerror(errno)); return NULL; } if (FD_ISSET(STDIN_FILENO, &fds)) { fgets(astresp, sizeof(astresp), stdin); if (feof(stdin)) { fprintf(stderr, "Got hungup on apparently\n"); return NULL; } astresp[strlen(astresp) - 1] = '\0'; fprintf(stderr, "Ooh, got a response from Asterisk: '%s'\n", astresp); return astresp; } if (FD_ISSET(AUDIO_FILENO, &fds)) { res = read(AUDIO_FILENO, audiobuf, sizeof(audiobuf)); if (res > 0) { if (sphinx_sock > -1) write(sphinx_sock, audiobuf, res); } } if ((sphinx_sock > -1) && FD_ISSET(sphinx_sock, &fds)) { res = read(sphinx_sock, sphinxresp, sizeof(sphinxresp)); if (res > 0) { fprintf(stderr, "Oooh, Sphinx found a token: '%s'\n", sphinxresp); return sphinxresp; } else if (res == 0) { fprintf(stderr, "Hrm, lost sphinx, guess we're on our own\n"); close(sphinx_sock); sphinx_sock = -1; } } } } static char *run_command(char *command) { fprintf(stdout, "%s\n", command); return wait_result(); } static int run_script(void) { char *res; res = run_command("STREAM FILE demo-enterkeywords 0123456789*#"); if (!res) { fprintf(stderr, "Failed to execute command\n"); return -1; } fprintf(stderr, "1. Result is '%s'\n", res); res = run_command("STREAM FILE demo-nomatch 0123456789*#"); if (!res) { fprintf(stderr, "Failed to execute command\n"); return -1; } fprintf(stderr, "2. Result is '%s'\n", res); res = run_command("SAY NUMBER 23452345 0123456789*#"); if (!res) { fprintf(stderr, "Failed to execute command\n"); return -1; } fprintf(stderr, "3. Result is '%s'\n", res); res = run_command("GET DATA demo-enterkeywords"); if (!res) { fprintf(stderr, "Failed to execute command\n"); return -1; } fprintf(stderr, "4. Result is '%s'\n", res); res = run_command("STREAM FILE auth-thankyou \"\""); if (!res) { fprintf(stderr, "Failed to execute command\n"); return -1; } fprintf(stderr, "5. Result is '%s'\n", res); return 0; } int main(int argc, char *argv[]) { char *tmp; int ver = 0; int subver = 0; /* Setup stdin/stdout for line buffering */ setlinebuf(stdin); setlinebuf(stdout); if (read_environment()) { fprintf(stderr, "Failed to read environment: %s\n", strerror(errno)); exit(1); } connect_sphinx(); tmp = getenv("agi_enhanced"); if (tmp) { if (sscanf(tmp, "%d.%d", &ver, &subver) != 2) ver = 0; } if (ver < 1) { fprintf(stderr, "No enhanced AGI services available. Use EAGI, not AGI\n"); exit(1); } if (run_script()) return -1; exit(0); } asterisk-1.4.21.2/agi/jukebox.agi0000755000175000017500000003430210403144641015721 0ustar maniacmaniac#!/usr/bin/perl # # Jukebox 0.2 # # A music manager for Asterisk. # # Copyright (C) 2005-2006, Justin Tunney # # Justin Tunney # # This program is free software, distributed under the terms of the # GNU General Public License v2. # # Keep it open source pigs # # -------------------------------------------------------------------- # # Uses festival to list off all your MP3 music files over a channel in # a hierarchical fashion. Put this file in your agi-bin folder which # is located at: /var/lib/asterisk/agi-bin Be sure to chmod +x it! # # Invocation Example: # exten => 68742,1,Answer() # exten => 68742,2,agi,jukebox.agi|/home/justin/Music # exten => 68742,3,Hangup() # # exten => 68742,1,Answer() # exten => 68742,2,agi,jukebox.agi|/home/justin/Music|pm # exten => 68742,3,Hangup() # # Options: # p - Precache text2wave outputs for every possible filename. # It is much better to set this option because if a caller # presses a key during a cache operation, it will be ignored. # m - Go back to menu after playing song # g - Do not play the greeting message # # Usage Instructions: # - Press '*' to go up a directory. If you are in the root music # folder you will be exitted from the script. # - If you have a really long list of files, you can filter the list # at any time by pressing '#' and spelling out a few letters you # expect the files to start with. For example, if you wanted to # know what extension 'Requiem For A Dream' was, you'd type: # '#737'. Note, phone keypads don't include Q and Z. Q is 7 and # Z is 9. # # Notes: # - This AGI script uses the MP3Player command which uses the # mpg123 Program. Grab yourself a copy of this program by # going to http://www.mpg123.de/cgi-bin/sitexplorer.cgi?/mpg123/ # Be sure to download mpg123-0.59r.tar.gz because it is known to # work with Asterisk and hopefully isn't the release with that # awful security problem. If you're using Fedora Core 3 with # Alsa like me, make linux-alsa isn't going to work. Do make # linux-devel and you're peachy keen. # # - You won't get nifty STDERR debug messages if you're using a # remote asterisk shell. # # - For some reason, caching certain files will generate the # error: 'using default diphone ax-ax for y-pau'. Example: # # echo "Depeche Mode - CUW - 05 - The Meaning of Love" | text2wave -o /var/jukeboxcache/jukeboxcache/Depeche_Mode/Depeche_Mode_-_CUW_-_05_-_The_Meaning_of_Love.mp3.ul -otype ulaw - # The temporary work around is to just touch these files. # # - The background app doesn't like to get more than 2031 chars # of input. # use strict; $|=1; # Setup some variables my %AGI; my $tests = 0; my $fail = 0; my $pass = 0; my @masterCacheList = (); my $maxNumber = 10; while () { chomp; last unless length($_); if (/^agi_(\w+)\:\s+(.*)$/) { $AGI{$1} = $2; } } # setup options my $SHOWGREET = 1; my $PRECACHE = 0; my $MENUAFTERSONG = 0; $PRECACHE = 1 if $ARGV[1] =~ /p/; $MENUAFTERSONG = 1 if $ARGV[1] =~ /m/; $SHOWGREET = 0 if $ARGV[1] =~ /g/; # setup folders my $MUSIC = $ARGV[0]; $MUSIC = &rmts($MUSIC); my $FESTIVALCACHE = "/var/jukeboxcache"; if (! -e $FESTIVALCACHE) { `mkdir -p -m0776 $FESTIVALCACHE`; } # make sure we have some essential files if (! -e "$FESTIVALCACHE/jukebox_greet.ul") { `echo "Welcome to the Asterisk Jukebox" | text2wave -o $FESTIVALCACHE/jukebox_greet.ul -otype ulaw -`; } if (! -e "$FESTIVALCACHE/jukebox_press.ul") { `echo "Press" | text2wave -o $FESTIVALCACHE/jukebox_press.ul -otype ulaw -`; } if (! -e "$FESTIVALCACHE/jukebox_for.ul") { `echo "For" | text2wave -o $FESTIVALCACHE/jukebox_for.ul -otype ulaw -`; } if (! -e "$FESTIVALCACHE/jukebox_toplay.ul") { `echo "To play" | text2wave -o $FESTIVALCACHE/jukebox_toplay.ul -otype ulaw -`; } if (! -e "$FESTIVALCACHE/jukebox_nonefound.ul") { `echo "There were no music files found in this folder" | text2wave -o $FESTIVALCACHE/jukebox_nonefound.ul -otype ulaw -`; } if (! -e "$FESTIVALCACHE/jukebox_percent.ul") { `echo "Percent" | text2wave -o $FESTIVALCACHE/jukebox_percent.ul -otype ulaw -`; } if (! -e "$FESTIVALCACHE/jukebox_generate.ul") { `echo "Please wait while Astrisk Jukebox cashes the files of your music collection" | text2wave -o $FESTIVALCACHE/jukebox_generate.ul -otype ulaw -`; } if (! -e "$FESTIVALCACHE/jukebox_invalid.ul") { `echo "You have entered an invalid selection" | text2wave -o $FESTIVALCACHE/jukebox_invalid.ul -otype ulaw -`; } if (! -e "$FESTIVALCACHE/jukebox_thankyou.ul") { `echo "Thank you for using Astrisk Jukebox, Goodbye" | text2wave -o $FESTIVALCACHE/jukebox_thankyou.ul -otype ulaw -`; } # greet the user if ($SHOWGREET) { print "EXEC Playback \"$FESTIVALCACHE/jukebox_greet\"\n"; my $result = ; &check_result($result); } # go through the directories music_dir_cache() if $PRECACHE; music_dir_menu('/'); exit 0; ########################################################################## sub music_dir_menu { my $dir = shift; # generate a list of mp3's and directories and assign each one it's # own selection number. Then make sure that we've got a sound clip # for the file name if (!opendir(THEDIR, rmts($MUSIC.$dir))) { print STDERR "Failed to open music directory: $dir\n"; exit 1; } my @files = sort readdir THEDIR; my $cnt = 1; my @masterBgList = (); foreach my $file (@files) { chomp($file); if ($file ne '.' && $file ne '..' && $file ne 'festivalcache') { # ignore special files my $real_version = &rmts($MUSIC.$dir).'/'.$file; my $cache_version = &rmts($FESTIVALCACHE.$dir).'/'.$file.'.ul'; my $cache_version2 = &rmts($FESTIVALCACHE.$dir).'/'.$file; my $cache_version_esc = &clean_file($cache_version); my $cache_version2_esc = &clean_file($cache_version2); if (-d $real_version) { # 0:id 1:type 2:text2wav-file 3:for-filtering 4:the-directory 5:text2wav echo push(@masterBgList, [$cnt++, 1, $cache_version2_esc, &remove_special_chars($file), $file, "for the $file folder"]); } elsif ($real_version =~ /\.mp3$/) { # 0:id 1:type 2:text2wav-file 3:for-filtering 4:the-mp3 push(@masterBgList, [$cnt++, 2, $cache_version2_esc, &remove_special_chars($file), $real_version, "to play $file"]); } } } close(THEDIR); my @filterList = @masterBgList; if (@filterList == 0) { print "EXEC Playback \"$FESTIVALCACHE/jukebox_nonefound\"\n"; my $result = ; &check_result($result); return 0; } for (;;) { MYCONTINUE: # play bg selections and figure out their selection my $digit = ''; my $digitstr = ''; for (my $n=0; $n<@filterList; $n++) { &cache_speech(&remove_file_extension($filterList[$n][5]), "$filterList[$n][2].ul") if ! -e "$filterList[$n][2].ul"; &cache_speech("Press $filterList[$n][0]", "$FESTIVALCACHE/jukebox_$filterList[$n][0].ul") if ! -e "$FESTIVALCACHE/jukebox_$filterList[$n][0].ul"; print "EXEC Background \"$filterList[$n][2]&$FESTIVALCACHE/jukebox_$filterList[$n][0]\"\n"; my $result = ; $digit = &check_result($result); if ($digit > 0) { $digitstr .= chr($digit); last; } } for (;;) { print "WAIT FOR DIGIT 3000\n"; my $result = ; $digit = &check_result($result); last if $digit <= 0; $digitstr .= chr($digit); } # see if it's a valid selection print STDERR "Digits Entered: '$digitstr'\n"; exit 0 if $digitstr eq ''; my $found = 0; goto EXITSUB if $digitstr =~ /\*/; # filter the list if ($digitstr =~ /^\#\d+/) { my $regexp = ''; for (my $n=1; $n; &check_result($result); `rm $link`; if (!$MENUAFTERSONG) { print "EXEC Playback \"$FESTIVALCACHE/jukebox_thankyou\"\n"; my $result = ; &check_result($result); exit 0; } else { goto MYCONTINUE; } } } } print "EXEC Playback \"$FESTIVALCACHE/jukebox_invalid\"\n"; my $result = ; &check_result($result); } EXITSUB: } sub cache_speech { my $speech = shift; my $file = shift; my $theDir = extract_file_dir($file); `mkdir -p -m0776 $theDir`; print STDERR "echo \"$speech\" | text2wave -o $file -otype ulaw -\n"; my $cmdr = `echo "$speech" | text2wave -o $file -otype ulaw -`; chomp($cmdr); if ($cmdr =~ /using default diphone/) { # temporary bug work around.... `touch $file`; } elsif ($cmdr ne '') { print STDERR "Command Failed\n"; exit 1; } } sub music_dir_cache { # generate list of text2speech files to generate if (!music_dir_cache_genlist('/')) { print STDERR "Horrible Dreadful Error: No Music Found in $MUSIC!"; exit 1; } # add to list how many 'number' files we have to generate. We can't # use the SayNumber app in Asterisk because we want to chain all # talking in one Background command. We also want a consistent # voice... for (my $n=1; $n<=$maxNumber; $n++) { push(@masterCacheList, [3, "Press $n", "$FESTIVALCACHE/jukebox_$n.ul"]) if ! -e "$FESTIVALCACHE/jukebox_$n.ul"; } # now generate all these darn text2speech files if (@masterCacheList > 5) { print "EXEC Playback \"$FESTIVALCACHE/jukebox_generate\"\n"; my $result = ; &check_result($result); } my $theTime = time(); for (my $n=0; $n < @masterCacheList; $n++) { my $cmdr = ''; if ($masterCacheList[$n][0] == 1) { # directory &cache_speech("for folder $masterCacheList[$n][1]", $masterCacheList[$n][2]); } elsif ($masterCacheList[$n][0] == 2) { # file &cache_speech("to play $masterCacheList[$n][1]", $masterCacheList[$n][2]); } elsif ($masterCacheList[$n][0] == 3) { # number &cache_speech($masterCacheList[$n][1], $masterCacheList[$n][2]); } if (time() >= $theTime + 30) { my $percent = int($n / @masterCacheList * 100); print "SAY NUMBER $percent \"\"\n"; my $result = ; &check_result($result); print "EXEC Playback \"$FESTIVALCACHE/jukebox_percent\"\n"; my $result = ; &check_result($result); $theTime = time(); } } } # this function will fill the @masterCacheList of all the files that # need to have text2speeced ulaw files of their names generated sub music_dir_cache_genlist { my $dir = shift; if (!opendir(THEDIR, rmts($MUSIC.$dir))) { print STDERR "Failed to open music directory: $dir\n"; exit 1; } my @files = sort readdir THEDIR; my $foundFiles = 0; my $tmpMaxNum = 0; foreach my $file (@files) { chomp; if ($file ne '.' && $file ne '..' && $file ne 'festivalcache') { # ignore special files my $real_version = &rmts($MUSIC.$dir).'/'.$file; my $cache_version = &rmts($FESTIVALCACHE.$dir).'/'.$file.'.ul'; my $cache_version2 = &rmts($FESTIVALCACHE.$dir).'/'.$file; my $cache_version_esc = &clean_file($cache_version); my $cache_version2_esc = &clean_file($cache_version2); if (-d $real_version) { if (music_dir_cache_genlist(rmts($dir).'/'.$file)) { $tmpMaxNum++; $maxNumber = $tmpMaxNum if $tmpMaxNum > $maxNumber; push(@masterCacheList, [1, $file, $cache_version_esc]) if ! -e $cache_version_esc; $foundFiles = 1; } } elsif ($real_version =~ /\.mp3$/) { $tmpMaxNum++; $maxNumber = $tmpMaxNum if $tmpMaxNum > $maxNumber; push(@masterCacheList, [2, &remove_file_extension($file), $cache_version_esc]) if ! -e $cache_version_esc; $foundFiles = 1; } } } close(THEDIR); return $foundFiles; } sub rmts { # remove trailing slash my $hog = shift; $hog =~ s/\/$//; return $hog; } sub extract_file_name { my $hog = shift; $hog =~ /\/?([^\/]+)$/; return $1; } sub extract_file_dir { my $hog = shift; return $hog if ! ($hog =~ /\//); $hog =~ /(.*)\/[^\/]*$/; return $1; } sub remove_file_extension { my $hog = shift; return $hog if ! ($hog =~ /\./); $hog =~ /(.*)\.[^.]*$/; return $1; } sub clean_file { my $hog = shift; $hog =~ s/\\/_/g; $hog =~ s/ /_/g; $hog =~ s/\t/_/g; $hog =~ s/\'/_/g; $hog =~ s/\"/_/g; $hog =~ s/\(/_/g; $hog =~ s/\)/_/g; $hog =~ s/&/_/g; $hog =~ s/\[/_/g; $hog =~ s/\]/_/g; $hog =~ s/\$/_/g; $hog =~ s/\|/_/g; $hog =~ s/\^/_/g; return $hog; } sub remove_special_chars { my $hog = shift; $hog =~ s/\\//g; $hog =~ s/ //g; $hog =~ s/\t//g; $hog =~ s/\'//g; $hog =~ s/\"//g; $hog =~ s/\(//g; $hog =~ s/\)//g; $hog =~ s/&//g; $hog =~ s/\[//g; $hog =~ s/\]//g; $hog =~ s/\$//g; $hog =~ s/\|//g; $hog =~ s/\^//g; return $hog; } sub escape_file { my $hog = shift; $hog =~ s/\\/\\\\/g; $hog =~ s/ /\\ /g; $hog =~ s/\t/\\\t/g; $hog =~ s/\'/\\\'/g; $hog =~ s/\"/\\\"/g; $hog =~ s/\(/\\\(/g; $hog =~ s/\)/\\\)/g; $hog =~ s/&/\\&/g; $hog =~ s/\[/\\\[/g; $hog =~ s/\]/\\\]/g; $hog =~ s/\$/\\\$/g; $hog =~ s/\|/\\\|/g; $hog =~ s/\^/\\\^/g; return $hog; } sub check_result { my ($res) = @_; my $retval; $tests++; chomp $res; if ($res =~ /^200/) { $res =~ /result=(-?\d+)/; if (!length($1)) { print STDERR "FAIL ($res)\n"; $fail++; exit 1; } else { print STDERR "PASS ($1)\n"; return $1; } } else { print STDERR "FAIL (unexpected result '$res')\n"; exit 1; } } asterisk-1.4.21.2/agi/DialAnMp3.agi0000644000175000017500000000334010374426007015763 0ustar maniacmaniac#!/usr/bin/perl # # Simple AGI application to play mp3's selected by a user both using # xmms and over the phone itself. # $|=1; while() { chomp; last unless length($_); if (/^agi_(\w+)\:\s+(.*)$/) { $AGI{$1} = $2; } } print STDERR "AGI Environment Dump:\n"; foreach $i (sort keys %AGI) { print STDERR " -- $i = $AGI{$i}\n"; } dbmopen(%DIGITS, "/var/lib/asterisk/mp3list", 0644) || die("Unable to open mp3list");; sub checkresult { my ($res) = @_; my $retval; $tests++; chomp $res; if ($res =~ /^200/) { $res =~ /result=(-?[\w\*\#]+)/; return $1; } else { return -1; } } #print STDERR "1. Playing beep...\n"; #print "STREAM FILE beep \"\"\n"; #$result = ; #checkresult($result); print STDERR "2. Getting song name...\n"; print "GET DATA demo-enterkeywords\n"; $result = ; $digitstr = checkresult($result); if ($digitstr < 0) { exit(1); } $digitstr =~ s/\*/ /g; print STDERR "Resulting songname is $digitstr\n"; @searchwords = split (/\s+/, $digitstr); print STDERR "Searchwords: " . join(':', @searchwords) . "\n"; foreach $key (sort keys %DIGITS) { @words = split(/\s+/, $DIGITS{$key}); $match = 1; foreach $search (@searchwords) { $match = 0 unless grep(/$search/, @words); } if ($match > 0) { print STDERR "File $key matches\n"; # Play a beep print "STREAM FILE beep \"\"\n"; system("xmms", $key); $result = ; if (&checkresult($result) < 0) { exit 0; } print "EXEC MP3Player \"$key\"\n"; # print "WAIT FOR DIGIT 60000\n"; $result = ; if (&checkresult($result) < 0) { exit 0; } print STDERR "Got here...\n"; } } print STDERR "4. Testing 'saynumber' of $digitstr...\n"; print "STREAM FILE demo-nomatch\"\"\n"; $result = ; checkresult($result); asterisk-1.4.21.2/agi/agi-test.agi0000644000175000017500000000331610374426007015773 0ustar maniacmaniac#!/usr/bin/perl use strict; $|=1; # Setup some variables my %AGI; my $tests = 0; my $fail = 0; my $pass = 0; while() { chomp; last unless length($_); if (/^agi_(\w+)\:\s+(.*)$/) { $AGI{$1} = $2; } } print STDERR "AGI Environment Dump:\n"; foreach my $i (sort keys %AGI) { print STDERR " -- $i = $AGI{$i}\n"; } sub checkresult { my ($res) = @_; my $retval; $tests++; chomp $res; if ($res =~ /^200/) { $res =~ /result=(-?\d+)/; if (!length($1)) { print STDERR "FAIL ($res)\n"; $fail++; } else { print STDERR "PASS ($1)\n"; $pass++; } } else { print STDERR "FAIL (unexpected result '$res')\n"; $fail++; } } print STDERR "1. Testing 'sendfile'..."; print "STREAM FILE beep \"\"\n"; my $result = ; &checkresult($result); print STDERR "2. Testing 'sendtext'..."; print "SEND TEXT \"hello world\"\n"; my $result = ; &checkresult($result); print STDERR "3. Testing 'sendimage'..."; print "SEND IMAGE asterisk-image\n"; my $result = ; &checkresult($result); print STDERR "4. Testing 'saynumber'..."; print "SAY NUMBER 192837465 \"\"\n"; my $result = ; &checkresult($result); print STDERR "5. Testing 'waitdtmf'..."; print "WAIT FOR DIGIT 1000\n"; my $result = ; &checkresult($result); print STDERR "6. Testing 'record'..."; print "RECORD FILE testagi gsm 1234 3000\n"; my $result = ; &checkresult($result); print STDERR "6a. Testing 'record' playback..."; print "STREAM FILE testagi \"\"\n"; my $result = ; &checkresult($result); print STDERR "================== Complete ======================\n"; print STDERR "$tests tests completed, $pass passed, $fail failed\n"; print STDERR "==================================================\n"; asterisk-1.4.21.2/res/0000755000175000017500000000000011162660652013624 5ustar maniacmaniacasterisk-1.4.21.2/res/Makefile0000644000175000017500000000174410752317240015266 0ustar maniacmaniac# # Asterisk -- A telephony toolkit for Linux. # # Makefile for resource modules # # Copyright (C) 1999-2006, Digium, Inc. # # This program is free software, distributed under the terms of # the GNU General Public License # -include ../menuselect.makeopts ../menuselect.makedeps MENUSELECT_CATEGORY=RES MENUSELECT_DESCRIPTION=Resource Modules ALL_C_MODS:=$(patsubst %.c,%,$(wildcard res_*.c)) ALL_CC_MODS:=$(patsubst %.cc,%,$(wildcard res_*.cc)) C_MODS:=$(filter-out $(MENUSELECT_RES),$(ALL_C_MODS)) CC_MODS:=$(filter-out $(MENUSELECT_RES),$(ALL_CC_MODS)) LOADABLE_MODS:=$(C_MODS) $(CC_MODS) ifneq ($(findstring res,$(MENUSELECT_EMBED)),) EMBEDDED_MODS:=$(LOADABLE_MODS) LOADABLE_MODS:= endif all: _all include $(ASTTOPDIR)/Makefile.moddir_rules snmp/agent.o: ASTCFLAGS+=$(MENUSELECT_OPTS_res_snmp:%=-D%) $(foreach dep,$(MENUSELECT_DEPENDS_res_snmp),$(value $(dep)_INCLUDE)) $(if $(filter res_snmp,$(EMBEDDED_MODS)),modules.link,res_snmp.so): snmp/agent.o clean:: rm -f snmp/*.o asterisk-1.4.21.2/res/res_config_pgsql.c0000644000175000017500000006213211005653047017314 0ustar maniacmaniac/* * Asterisk -- A telephony toolkit for Linux. * * Copyright (C) 1999-2005, Digium, Inc. * * Manuel Guesdon - Postgresql RealTime Driver Author/Adaptor * Mark Spencer - Asterisk Author * Matthew Boehm - MySQL RealTime Driver Author * * res_config_pgsql.c * * v1.0 - (07-11-05) - Initial version based on res_config_mysql v2.0 */ /*! \file * * \brief Postgresql plugin for Asterisk RealTime Architecture * * \author Mark Spencer * \author Manuel Guesdon - Postgresql RealTime Driver Author/Adaptor * * \arg http://www.postgresql.org */ /*** MODULEINFO pgsql ***/ #include "asterisk.h" ASTERISK_FILE_VERSION(__FILE__, "$Revision: 114829 $") #include #include #include #include /* PostgreSQL */ #include "asterisk/file.h" #include "asterisk/logger.h" #include "asterisk/channel.h" #include "asterisk/pbx.h" #include "asterisk/config.h" #include "asterisk/module.h" #include "asterisk/lock.h" #include "asterisk/options.h" #include "asterisk/utils.h" #include "asterisk/cli.h" AST_MUTEX_DEFINE_STATIC(pgsql_lock); #define RES_CONFIG_PGSQL_CONF "res_pgsql.conf" PGconn *pgsqlConn = NULL; #define MAX_DB_OPTION_SIZE 64 static char dbhost[MAX_DB_OPTION_SIZE] = ""; static char dbuser[MAX_DB_OPTION_SIZE] = ""; static char dbpass[MAX_DB_OPTION_SIZE] = ""; static char dbname[MAX_DB_OPTION_SIZE] = ""; static char dbsock[MAX_DB_OPTION_SIZE] = ""; static int dbport = 5432; static time_t connect_time = 0; static int parse_config(void); static int pgsql_reconnect(const char *database); static int realtime_pgsql_status(int fd, int argc, char **argv); static char cli_realtime_pgsql_status_usage[] = "Usage: realtime pgsql status\n" " Shows connection information for the Postgresql RealTime driver\n"; static struct ast_cli_entry cli_realtime[] = { { { "realtime", "pgsql", "status", NULL }, realtime_pgsql_status, "Shows connection information for the Postgresql RealTime driver", cli_realtime_pgsql_status_usage }, }; static struct ast_variable *realtime_pgsql(const char *database, const char *table, va_list ap) { PGresult *result = NULL; int num_rows = 0, pgerror; char sql[256], escapebuf[513]; char *stringp; char *chunk; char *op; const char *newparam, *newval; struct ast_variable *var = NULL, *prev = NULL; if (!table) { ast_log(LOG_WARNING, "Postgresql RealTime: No table specified.\n"); return NULL; } /* Get the first parameter and first value in our list of passed paramater/value pairs */ newparam = va_arg(ap, const char *); newval = va_arg(ap, const char *); if (!newparam || !newval) { ast_log(LOG_WARNING, "Postgresql RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n"); if (pgsqlConn) { PQfinish(pgsqlConn); pgsqlConn = NULL; }; return NULL; } /* Create the first part of the query using the first parameter/value pairs we just extracted If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */ op = strchr(newparam, ' ') ? "" : " ="; PQescapeStringConn(pgsqlConn, escapebuf, newval, (sizeof(escapebuf) - 1) / 2, &pgerror); if (pgerror) { ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval); va_end(ap); return NULL; } snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op, escapebuf); while ((newparam = va_arg(ap, const char *))) { newval = va_arg(ap, const char *); if (!strchr(newparam, ' ')) op = " ="; else op = ""; PQescapeStringConn(pgsqlConn, escapebuf, newval, (sizeof(escapebuf) - 1) / 2, &pgerror); if (pgerror) { ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval); va_end(ap); return NULL; } snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s '%s'", newparam, op, escapebuf); } va_end(ap); /* We now have our complete statement; Lets connect to the server and execute it. */ ast_mutex_lock(&pgsql_lock); if (!pgsql_reconnect(database)) { ast_mutex_unlock(&pgsql_lock); return NULL; } if (!(result = PQexec(pgsqlConn, sql))) { ast_log(LOG_WARNING, "Postgresql RealTime: Failed to query database. Check debug for more info.\n"); ast_log(LOG_DEBUG, "Postgresql RealTime: Query: %s\n", sql); ast_log(LOG_DEBUG, "Postgresql RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn)); ast_mutex_unlock(&pgsql_lock); return NULL; } else { ExecStatusType result_status = PQresultStatus(result); if (result_status != PGRES_COMMAND_OK && result_status != PGRES_TUPLES_OK && result_status != PGRES_NONFATAL_ERROR) { ast_log(LOG_WARNING, "Postgresql RealTime: Failed to query database. Check debug for more info.\n"); ast_log(LOG_DEBUG, "Postgresql RealTime: Query: %s\n", sql); ast_log(LOG_DEBUG, "Postgresql RealTime: Query Failed because: %s (%s)\n", PQresultErrorMessage(result), PQresStatus(result_status)); ast_mutex_unlock(&pgsql_lock); return NULL; } } ast_log(LOG_DEBUG, "1Postgresql RealTime: Result=%p Query: %s\n", result, sql); if ((num_rows = PQntuples(result)) > 0) { int i = 0; int rowIndex = 0; int numFields = PQnfields(result); char **fieldnames = NULL; ast_log(LOG_DEBUG, "Postgresql RealTime: Found %d rows.\n", num_rows); if (!(fieldnames = ast_calloc(1, numFields * sizeof(char *)))) { ast_mutex_unlock(&pgsql_lock); PQclear(result); return NULL; } for (i = 0; i < numFields; i++) fieldnames[i] = PQfname(result, i); for (rowIndex = 0; rowIndex < num_rows; rowIndex++) { for (i = 0; i < numFields; i++) { stringp = PQgetvalue(result, rowIndex, i); while (stringp) { chunk = strsep(&stringp, ";"); if (chunk && !ast_strlen_zero(ast_strip(chunk))) { if (prev) { prev->next = ast_variable_new(fieldnames[i], chunk); if (prev->next) { prev = prev->next; } } else { prev = var = ast_variable_new(fieldnames[i], chunk); } } } } } ast_free(fieldnames); } else { ast_log(LOG_DEBUG, "Postgresql RealTime: Could not find any rows in table %s.\n", table); } ast_mutex_unlock(&pgsql_lock); PQclear(result); return var; } static struct ast_config *realtime_multi_pgsql(const char *database, const char *table, va_list ap) { PGresult *result = NULL; int num_rows = 0, pgerror; char sql[256], escapebuf[513]; const char *initfield = NULL; char *stringp; char *chunk; char *op; const char *newparam, *newval; struct ast_realloca ra; struct ast_variable *var = NULL; struct ast_config *cfg = NULL; struct ast_category *cat = NULL; if (!table) { ast_log(LOG_WARNING, "Postgresql RealTime: No table specified.\n"); return NULL; } memset(&ra, 0, sizeof(ra)); if (!(cfg = ast_config_new())) return NULL; /* Get the first parameter and first value in our list of passed paramater/value pairs */ newparam = va_arg(ap, const char *); newval = va_arg(ap, const char *); if (!newparam || !newval) { ast_log(LOG_WARNING, "Postgresql RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n"); if (pgsqlConn) { PQfinish(pgsqlConn); pgsqlConn = NULL; }; return NULL; } initfield = ast_strdupa(newparam); if ((op = strchr(initfield, ' '))) { *op = '\0'; } /* Create the first part of the query using the first parameter/value pairs we just extracted If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */ if (!strchr(newparam, ' ')) op = " ="; else op = ""; PQescapeStringConn(pgsqlConn, escapebuf, newval, (sizeof(escapebuf) - 1) / 2, &pgerror); if (pgerror) { ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval); va_end(ap); return NULL; } snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op, escapebuf); while ((newparam = va_arg(ap, const char *))) { newval = va_arg(ap, const char *); if (!strchr(newparam, ' ')) op = " ="; else op = ""; PQescapeStringConn(pgsqlConn, escapebuf, newval, (sizeof(escapebuf) - 1) / 2, &pgerror); if (pgerror) { ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval); va_end(ap); return NULL; } snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s '%s'", newparam, op, escapebuf); } if (initfield) { snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " ORDER BY %s", initfield); } va_end(ap); /* We now have our complete statement; Lets connect to the server and execute it. */ ast_mutex_lock(&pgsql_lock); if (!pgsql_reconnect(database)) { ast_mutex_unlock(&pgsql_lock); return NULL; } if (!(result = PQexec(pgsqlConn, sql))) { ast_log(LOG_WARNING, "Postgresql RealTime: Failed to query database. Check debug for more info.\n"); ast_log(LOG_DEBUG, "Postgresql RealTime: Query: %s\n", sql); ast_log(LOG_DEBUG, "Postgresql RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn)); ast_mutex_unlock(&pgsql_lock); return NULL; } else { ExecStatusType result_status = PQresultStatus(result); if (result_status != PGRES_COMMAND_OK && result_status != PGRES_TUPLES_OK && result_status != PGRES_NONFATAL_ERROR) { ast_log(LOG_WARNING, "Postgresql RealTime: Failed to query database. Check debug for more info.\n"); ast_log(LOG_DEBUG, "Postgresql RealTime: Query: %s\n", sql); ast_log(LOG_DEBUG, "Postgresql RealTime: Query Failed because: %s (%s)\n", PQresultErrorMessage(result), PQresStatus(result_status)); ast_mutex_unlock(&pgsql_lock); return NULL; } } ast_log(LOG_DEBUG, "2Postgresql RealTime: Result=%p Query: %s\n", result, sql); if ((num_rows = PQntuples(result)) > 0) { int numFields = PQnfields(result); int i = 0; int rowIndex = 0; char **fieldnames = NULL; ast_log(LOG_DEBUG, "Postgresql RealTime: Found %d rows.\n", num_rows); if (!(fieldnames = ast_calloc(1, numFields * sizeof(char *)))) { ast_mutex_unlock(&pgsql_lock); PQclear(result); return NULL; } for (i = 0; i < numFields; i++) fieldnames[i] = PQfname(result, i); for (rowIndex = 0; rowIndex < num_rows; rowIndex++) { var = NULL; if (!(cat = ast_category_new(""))) continue; for (i = 0; i < numFields; i++) { stringp = PQgetvalue(result, rowIndex, i); while (stringp) { chunk = strsep(&stringp, ";"); if (chunk && !ast_strlen_zero(ast_strip(chunk))) { if (initfield && !strcmp(initfield, fieldnames[i])) { ast_category_rename(cat, chunk); } var = ast_variable_new(fieldnames[i], chunk); ast_variable_append(cat, var); } } } ast_category_append(cfg, cat); } ast_free(fieldnames); } else { ast_log(LOG_WARNING, "Postgresql RealTime: Could not find any rows in table %s.\n", table); } ast_mutex_unlock(&pgsql_lock); PQclear(result); return cfg; } static int update_pgsql(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap) { PGresult *result = NULL; int numrows = 0, pgerror; char sql[256], escapebuf[513]; const char *newparam, *newval; if (!table) { ast_log(LOG_WARNING, "Postgresql RealTime: No table specified.\n"); return -1; } /* Get the first parameter and first value in our list of passed paramater/value pairs */ newparam = va_arg(ap, const char *); newval = va_arg(ap, const char *); if (!newparam || !newval) { ast_log(LOG_WARNING, "Postgresql RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n"); if (pgsqlConn) { PQfinish(pgsqlConn); pgsqlConn = NULL; }; return -1; } /* Create the first part of the query using the first parameter/value pairs we just extracted If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */ PQescapeStringConn(pgsqlConn, escapebuf, newval, (sizeof(escapebuf) - 1) / 2, &pgerror); if (pgerror) { ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval); va_end(ap); return -1; } snprintf(sql, sizeof(sql), "UPDATE %s SET %s = '%s'", table, newparam, escapebuf); while ((newparam = va_arg(ap, const char *))) { newval = va_arg(ap, const char *); PQescapeStringConn(pgsqlConn, escapebuf, newval, (sizeof(escapebuf) - 1) / 2, &pgerror); if (pgerror) { ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval); va_end(ap); return -1; } snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", %s = '%s'", newparam, escapebuf); } va_end(ap); PQescapeStringConn(pgsqlConn, escapebuf, lookup, (sizeof(escapebuf) - 1) / 2, &pgerror); if (pgerror) { ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", lookup); va_end(ap); return -1; } snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " WHERE %s = '%s'", keyfield, escapebuf); ast_log(LOG_DEBUG, "Postgresql RealTime: Update SQL: %s\n", sql); /* We now have our complete statement; Lets connect to the server and execute it. */ ast_mutex_lock(&pgsql_lock); if (!pgsql_reconnect(database)) { ast_mutex_unlock(&pgsql_lock); return -1; } if (!(result = PQexec(pgsqlConn, sql))) { ast_log(LOG_WARNING, "Postgresql RealTime: Failed to query database. Check debug for more info.\n"); ast_log(LOG_DEBUG, "Postgresql RealTime: Query: %s\n", sql); ast_log(LOG_DEBUG, "Postgresql RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn)); ast_mutex_unlock(&pgsql_lock); return -1; } else { ExecStatusType result_status = PQresultStatus(result); if (result_status != PGRES_COMMAND_OK && result_status != PGRES_TUPLES_OK && result_status != PGRES_NONFATAL_ERROR) { ast_log(LOG_WARNING, "Postgresql RealTime: Failed to query database. Check debug for more info.\n"); ast_log(LOG_DEBUG, "Postgresql RealTime: Query: %s\n", sql); ast_log(LOG_DEBUG, "Postgresql RealTime: Query Failed because: %s (%s)\n", PQresultErrorMessage(result), PQresStatus(result_status)); ast_mutex_unlock(&pgsql_lock); return -1; } } numrows = atoi(PQcmdTuples(result)); ast_mutex_unlock(&pgsql_lock); ast_log(LOG_DEBUG, "Postgresql RealTime: Updated %d rows on table: %s\n", numrows, table); /* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html * An integer greater than zero indicates the number of rows affected * Zero indicates that no records were updated * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.) */ if (numrows >= 0) return (int) numrows; return -1; } static struct ast_config *config_pgsql(const char *database, const char *table, const char *file, struct ast_config *cfg, int withcomments) { PGresult *result = NULL; long num_rows; struct ast_variable *new_v; struct ast_category *cur_cat = NULL; char sqlbuf[1024] = ""; char *sql = sqlbuf; size_t sqlleft = sizeof(sqlbuf); char last[80] = ""; int last_cat_metric = 0; last[0] = '\0'; if (!file || !strcmp(file, RES_CONFIG_PGSQL_CONF)) { ast_log(LOG_WARNING, "Postgresql RealTime: Cannot configure myself.\n"); return NULL; } ast_build_string(&sql, &sqlleft, "SELECT category, var_name, var_val, cat_metric FROM %s ", table); ast_build_string(&sql, &sqlleft, "WHERE filename='%s' and commented=0", file); ast_build_string(&sql, &sqlleft, "ORDER BY cat_metric DESC, var_metric ASC, category, var_name "); ast_log(LOG_DEBUG, "Postgresql RealTime: Static SQL: %s\n", sqlbuf); /* We now have our complete statement; Lets connect to the server and execute it. */ ast_mutex_lock(&pgsql_lock); if (!pgsql_reconnect(database)) { ast_mutex_unlock(&pgsql_lock); return NULL; } if (!(result = PQexec(pgsqlConn, sqlbuf))) { ast_log(LOG_WARNING, "Postgresql RealTime: Failed to query database. Check debug for more info.\n"); ast_log(LOG_DEBUG, "Postgresql RealTime: Query: %s\n", sql); ast_log(LOG_DEBUG, "Postgresql RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn)); ast_mutex_unlock(&pgsql_lock); return NULL; } else { ExecStatusType result_status = PQresultStatus(result); if (result_status != PGRES_COMMAND_OK && result_status != PGRES_TUPLES_OK && result_status != PGRES_NONFATAL_ERROR) { ast_log(LOG_WARNING, "Postgresql RealTime: Failed to query database. Check debug for more info.\n"); ast_log(LOG_DEBUG, "Postgresql RealTime: Query: %s\n", sql); ast_log(LOG_DEBUG, "Postgresql RealTime: Query Failed because: %s (%s)\n", PQresultErrorMessage(result), PQresStatus(result_status)); ast_mutex_unlock(&pgsql_lock); return NULL; } } if ((num_rows = PQntuples(result)) > 0) { int rowIndex = 0; ast_log(LOG_DEBUG, "Postgresql RealTime: Found %ld rows.\n", num_rows); for (rowIndex = 0; rowIndex < num_rows; rowIndex++) { char *field_category = PQgetvalue(result, rowIndex, 0); char *field_var_name = PQgetvalue(result, rowIndex, 1); char *field_var_val = PQgetvalue(result, rowIndex, 2); char *field_cat_metric = PQgetvalue(result, rowIndex, 3); if (!strcmp(field_var_name, "#include")) { if (!ast_config_internal_load(field_var_val, cfg, 0)) { PQclear(result); ast_mutex_unlock(&pgsql_lock); return NULL; } continue; } if (strcmp(last, field_category) || last_cat_metric != atoi(field_cat_metric)) { cur_cat = ast_category_new(field_category); if (!cur_cat) break; strcpy(last, field_category); last_cat_metric = atoi(field_cat_metric); ast_category_append(cfg, cur_cat); } new_v = ast_variable_new(field_var_name, field_var_val); ast_variable_append(cur_cat, new_v); } } else { ast_log(LOG_WARNING, "Postgresql RealTime: Could not find config '%s' in database.\n", file); } PQclear(result); ast_mutex_unlock(&pgsql_lock); return cfg; } static struct ast_config_engine pgsql_engine = { .name = "pgsql", .load_func = config_pgsql, .realtime_func = realtime_pgsql, .realtime_multi_func = realtime_multi_pgsql, .update_func = update_pgsql }; static int load_module(void) { if(!parse_config()) return AST_MODULE_LOAD_DECLINE; ast_mutex_lock(&pgsql_lock); if (!pgsql_reconnect(NULL)) { ast_log(LOG_WARNING, "Postgresql RealTime: Couldn't establish connection. Check debug.\n"); ast_log(LOG_DEBUG, "Postgresql RealTime: Cannot Connect: %s\n", PQerrorMessage(pgsqlConn)); } ast_config_engine_register(&pgsql_engine); if (option_verbose) { ast_verbose("Postgresql RealTime driver loaded.\n"); } ast_cli_register_multiple(cli_realtime, sizeof(cli_realtime) / sizeof(struct ast_cli_entry)); ast_mutex_unlock(&pgsql_lock); return 0; } static int unload_module(void) { /* Aquire control before doing anything to the module itself. */ ast_mutex_lock(&pgsql_lock); if (pgsqlConn) { PQfinish(pgsqlConn); pgsqlConn = NULL; }; ast_cli_unregister_multiple(cli_realtime, sizeof(cli_realtime) / sizeof(struct ast_cli_entry)); ast_config_engine_deregister(&pgsql_engine); if (option_verbose) { ast_verbose("Postgresql RealTime unloaded.\n"); } ast_module_user_hangup_all(); /* Unlock so something else can destroy the lock. */ ast_mutex_unlock(&pgsql_lock); return 0; } static int reload(void) { /* Aquire control before doing anything to the module itself. */ ast_mutex_lock(&pgsql_lock); if (pgsqlConn) { PQfinish(pgsqlConn); pgsqlConn = NULL; }; parse_config(); if (!pgsql_reconnect(NULL)) { ast_log(LOG_WARNING, "Postgresql RealTime: Couldn't establish connection. Check debug.\n"); ast_log(LOG_DEBUG, "Postgresql RealTime: Cannot Connect: %s\n", PQerrorMessage(pgsqlConn)); } ast_verbose(VERBOSE_PREFIX_2 "Postgresql RealTime reloaded.\n"); /* Done reloading. Release lock so others can now use driver. */ ast_mutex_unlock(&pgsql_lock); return 0; } static int parse_config(void) { struct ast_config *config; const char *s; config = ast_config_load(RES_CONFIG_PGSQL_CONF); if (!config) { ast_log(LOG_WARNING, "Unable to load config %s\n",RES_CONFIG_PGSQL_CONF); return 0; } if (!(s = ast_variable_retrieve(config, "general", "dbuser"))) { ast_log(LOG_WARNING, "Postgresql RealTime: No database user found, using 'asterisk' as default.\n"); strcpy(dbuser, "asterisk"); } else { ast_copy_string(dbuser, s, sizeof(dbuser)); } if (!(s = ast_variable_retrieve(config, "general", "dbpass"))) { ast_log(LOG_WARNING, "Postgresql RealTime: No database password found, using 'asterisk' as default.\n"); strcpy(dbpass, "asterisk"); } else { ast_copy_string(dbpass, s, sizeof(dbpass)); } if (!(s = ast_variable_retrieve(config, "general", "dbhost"))) { ast_log(LOG_WARNING, "Postgresql RealTime: No database host found, using localhost via socket.\n"); dbhost[0] = '\0'; } else { ast_copy_string(dbhost, s, sizeof(dbhost)); } if (!(s = ast_variable_retrieve(config, "general", "dbname"))) { ast_log(LOG_WARNING, "Postgresql RealTime: No database name found, using 'asterisk' as default.\n"); strcpy(dbname, "asterisk"); } else { ast_copy_string(dbname, s, sizeof(dbname)); } if (!(s = ast_variable_retrieve(config, "general", "dbport"))) { ast_log(LOG_WARNING, "Postgresql RealTime: No database port found, using 5432 as default.\n"); dbport = 5432; } else { dbport = atoi(s); } if (!ast_strlen_zero(dbhost)) { /* No socket needed */ } else if (!(s = ast_variable_retrieve(config, "general", "dbsock"))) { ast_log(LOG_WARNING, "Postgresql RealTime: No database socket found, using '/tmp/pgsql.sock' as default.\n"); strcpy(dbsock, "/tmp/pgsql.sock"); } else { ast_copy_string(dbsock, s, sizeof(dbsock)); } ast_config_destroy(config); if (!ast_strlen_zero(dbhost)) { ast_log(LOG_DEBUG, "Postgresql RealTime Host: %s\n", dbhost); ast_log(LOG_DEBUG, "Postgresql RealTime Port: %i\n", dbport); } else { ast_log(LOG_DEBUG, "Postgresql RealTime Socket: %s\n", dbsock); } ast_log(LOG_DEBUG, "Postgresql RealTime User: %s\n", dbuser); ast_log(LOG_DEBUG, "Postgresql RealTime Password: %s\n", dbpass); ast_log(LOG_DEBUG, "Postgresql RealTime DBName: %s\n", dbname); return 1; } static int pgsql_reconnect(const char *database) { char my_database[50]; ast_copy_string(my_database, S_OR(database, dbname), sizeof(my_database)); /* mutex lock should have been locked before calling this function. */ if (pgsqlConn && PQstatus(pgsqlConn) != CONNECTION_OK) { PQfinish(pgsqlConn); pgsqlConn = NULL; } if ((!pgsqlConn) && (!ast_strlen_zero(dbhost) || !ast_strlen_zero(dbsock)) && !ast_strlen_zero(dbuser) && !ast_strlen_zero(dbpass) && !ast_strlen_zero(my_database)) { char *connInfo = NULL; unsigned int size = 100 + strlen(dbhost) + strlen(dbuser) + strlen(dbpass) + strlen(my_database); if (!(connInfo = ast_malloc(size))) return 0; sprintf(connInfo, "host=%s port=%d dbname=%s user=%s password=%s", dbhost, dbport, my_database, dbuser, dbpass); ast_log(LOG_DEBUG, "%u connInfo=%s\n", size, connInfo); pgsqlConn = PQconnectdb(connInfo); ast_log(LOG_DEBUG, "%u connInfo=%s\n", size, connInfo); ast_free(connInfo); connInfo = NULL; ast_log(LOG_DEBUG, "pgsqlConn=%p\n", pgsqlConn); if (pgsqlConn && PQstatus(pgsqlConn) == CONNECTION_OK) { ast_log(LOG_DEBUG, "Postgresql RealTime: Successfully connected to database.\n"); connect_time = time(NULL); return 1; } else { ast_log(LOG_ERROR, "Postgresql RealTime: Failed to connect database server %s on %s. Check debug for more info.\n", dbname, dbhost); ast_log(LOG_DEBUG, "Postgresql RealTime: Cannot Connect: %s\n", PQresultErrorMessage(NULL)); return 0; } } else { ast_log(LOG_DEBUG, "Postgresql RealTime: Everything is fine.\n"); return 1; } } static int realtime_pgsql_status(int fd, int argc, char **argv) { char status[256], status2[100] = ""; int ctime = time(NULL) - connect_time; if (pgsqlConn && PQstatus(pgsqlConn) == CONNECTION_OK) { if (!ast_strlen_zero(dbhost)) { snprintf(status, 255, "Connected to %s@%s, port %d", dbname, dbhost, dbport); } else if (!ast_strlen_zero(dbsock)) { snprintf(status, 255, "Connected to %s on socket file %s", dbname, dbsock); } else { snprintf(status, 255, "Connected to %s@%s", dbname, dbhost); } if (!ast_strlen_zero(dbuser)) { snprintf(status2, 99, " with username %s", dbuser); } if (ctime > 31536000) { ast_cli(fd, "%s%s for %d years, %d days, %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 31536000, (ctime % 31536000) / 86400, (ctime % 86400) / 3600, (ctime % 3600) / 60, ctime % 60); } else if (ctime > 86400) { ast_cli(fd, "%s%s for %d days, %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 86400, (ctime % 86400) / 3600, (ctime % 3600) / 60, ctime % 60); } else if (ctime > 3600) { ast_cli(fd, "%s%s for %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 3600, (ctime % 3600) / 60, ctime % 60); } else if (ctime > 60) { ast_cli(fd, "%s%s for %d minutes, %d seconds.\n", status, status2, ctime / 60, ctime % 60); } else { ast_cli(fd, "%s%s for %d seconds.\n", status, status2, ctime); } return RESULT_SUCCESS; } else { return RESULT_FAILURE; } } /* needs usecount semantics defined */ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "PostgreSQL RealTime Configuration Driver", .load = load_module, .unload = unload_module, .reload = reload ); asterisk-1.4.21.2/res/res_smdi.c0000644000175000017500000011536611022013234015570 0ustar maniacmaniac/* * Asterisk -- A telephony toolkit for Linux. * * Copyright (C) 2005-2008, Digium, Inc. * * Matthew A. Nicholson * Russell Bryant * * 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 SMDI support for Asterisk. * \author Matthew A. Nicholson * \author Russell Bryant * * Here is a useful mailing list post that describes SMDI protocol details: * \ref http://lists.digium.com/pipermail/asterisk-dev/2003-June/000884.html */ #include "asterisk.h" ASTERISK_FILE_VERSION(__FILE__, "$Revision: 120671 $") #include #include #include #include #include #include #include #include "asterisk/module.h" #include "asterisk/lock.h" #include "asterisk/utils.h" #include "asterisk/smdi.h" #include "asterisk/config.h" #include "asterisk/astobj.h" #include "asterisk/io.h" #include "asterisk/logger.h" #include "asterisk/utils.h" #include "asterisk/options.h" #include "asterisk/stringfields.h" #include "asterisk/linkedlists.h" #include "asterisk/app.h" #include "asterisk/pbx.h" /* Message expiry time in milliseconds */ #define SMDI_MSG_EXPIRY_TIME 30000 /* 30 seconds */ static const char config_file[] = "smdi.conf"; /*! \brief SMDI message desk message queue. */ struct ast_smdi_md_queue { ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_md_message); }; /*! \brief SMDI message waiting indicator message queue. */ struct ast_smdi_mwi_queue { ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_mwi_message); }; struct ast_smdi_interface { ASTOBJ_COMPONENTS_FULL(struct ast_smdi_interface, SMDI_MAX_FILENAME_LEN, 1); struct ast_smdi_md_queue md_q; ast_mutex_t md_q_lock; ast_cond_t md_q_cond; struct ast_smdi_mwi_queue mwi_q; ast_mutex_t mwi_q_lock; ast_cond_t mwi_q_cond; FILE *file; int fd; pthread_t thread; struct termios mode; int msdstrip; long msg_expiry; }; /*! \brief SMDI interface container. */ struct ast_smdi_interface_container { ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_interface); } smdi_ifaces; /*! \brief A mapping between an SMDI mailbox ID and an Asterisk mailbox */ struct mailbox_mapping { /*! This is the current state of the mailbox. It is simply on or * off to indicate if there are messages waiting or not. */ unsigned int cur_state:1; /*! A Pointer to the appropriate SMDI interface */ struct ast_smdi_interface *iface; AST_DECLARE_STRING_FIELDS( /*! The Name of the mailbox for the SMDI link. */ AST_STRING_FIELD(smdi); /*! The name of the mailbox on the Asterisk side */ AST_STRING_FIELD(mailbox); /*! The name of the voicemail context in use */ AST_STRING_FIELD(context); ); AST_LIST_ENTRY(mailbox_mapping) entry; }; /*! 10 seconds */ #define DEFAULT_POLLING_INTERVAL 10 /*! \brief Data that gets used by the SMDI MWI monitoring thread */ static struct { /*! The thread ID */ pthread_t thread; ast_mutex_t lock; ast_cond_t cond; /*! A list of mailboxes that need to be monitored */ AST_LIST_HEAD_NOLOCK(, mailbox_mapping) mailbox_mappings; /*! Polling Interval for checking mailbox status */ unsigned int polling_interval; /*! Set to 1 to tell the polling thread to stop */ unsigned int stop:1; /*! The time that the last poll began */ struct timeval last_poll; } mwi_monitor = { .thread = AST_PTHREADT_NULL, }; static void ast_smdi_interface_destroy(struct ast_smdi_interface *iface) { if (iface->thread != AST_PTHREADT_NULL && iface->thread != AST_PTHREADT_STOP) { pthread_cancel(iface->thread); pthread_join(iface->thread, NULL); } iface->thread = AST_PTHREADT_STOP; if (iface->file) fclose(iface->file); ASTOBJ_CONTAINER_DESTROYALL(&iface->md_q, ast_smdi_md_message_destroy); ASTOBJ_CONTAINER_DESTROYALL(&iface->mwi_q, ast_smdi_mwi_message_destroy); ASTOBJ_CONTAINER_DESTROY(&iface->md_q); ASTOBJ_CONTAINER_DESTROY(&iface->mwi_q); ast_mutex_destroy(&iface->md_q_lock); ast_cond_destroy(&iface->md_q_cond); ast_mutex_destroy(&iface->mwi_q_lock); ast_cond_destroy(&iface->mwi_q_cond); free(iface); ast_module_unref(ast_module_info->self); } void ast_smdi_interface_unref(struct ast_smdi_interface *iface) { ASTOBJ_UNREF(iface, ast_smdi_interface_destroy); } /*! * \internal * \brief Push an SMDI message to the back of an interface's message queue. * \param iface a pointer to the interface to use. * \param md_msg a pointer to the message to use. */ static void ast_smdi_md_message_push(struct ast_smdi_interface *iface, struct ast_smdi_md_message *md_msg) { ast_mutex_lock(&iface->md_q_lock); ASTOBJ_CONTAINER_LINK_END(&iface->md_q, md_msg); ast_cond_broadcast(&iface->md_q_cond); ast_mutex_unlock(&iface->md_q_lock); } /*! * \internal * \brief Push an SMDI message to the back of an interface's message queue. * \param iface a pointer to the interface to use. * \param mwi_msg a pointer to the message to use. */ static void ast_smdi_mwi_message_push(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *mwi_msg) { ast_mutex_lock(&iface->mwi_q_lock); ASTOBJ_CONTAINER_LINK_END(&iface->mwi_q, mwi_msg); ast_cond_broadcast(&iface->mwi_q_cond); ast_mutex_unlock(&iface->mwi_q_lock); } static int smdi_toggle_mwi(struct ast_smdi_interface *iface, const char *mailbox, int on) { FILE *file; int i; if (!(file = fopen(iface->name, "w"))) { ast_log(LOG_ERROR, "Error opening SMDI interface %s (%s) for writing\n", iface->name, strerror(errno)); return 1; } ASTOBJ_WRLOCK(iface); fprintf(file, "%s:MWI ", on ? "OP" : "RMV"); for (i = 0; i < iface->msdstrip; i++) fprintf(file, "0"); fprintf(file, "%s!\x04", mailbox); fclose(file); ASTOBJ_UNLOCK(iface); ast_log(LOG_DEBUG, "Sent MWI %s message for %s on %s\n", on ? "set" : "unset", mailbox, iface->name); return 0; } int ast_smdi_mwi_set(struct ast_smdi_interface *iface, const char *mailbox) { return smdi_toggle_mwi(iface, mailbox, 1); } int ast_smdi_mwi_unset(struct ast_smdi_interface *iface, const char *mailbox) { return smdi_toggle_mwi(iface, mailbox, 0); } void ast_smdi_md_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_md_message *md_msg) { ast_mutex_lock(&iface->md_q_lock); ASTOBJ_CONTAINER_LINK_START(&iface->md_q, md_msg); ast_cond_broadcast(&iface->md_q_cond); ast_mutex_unlock(&iface->md_q_lock); } void ast_smdi_mwi_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *mwi_msg) { ast_mutex_lock(&iface->mwi_q_lock); ASTOBJ_CONTAINER_LINK_START(&iface->mwi_q, mwi_msg); ast_cond_broadcast(&iface->mwi_q_cond); ast_mutex_unlock(&iface->mwi_q_lock); } enum smdi_message_type { SMDI_MWI, SMDI_MD, }; static inline int lock_msg_q(struct ast_smdi_interface *iface, enum smdi_message_type type) { switch (type) { case SMDI_MWI: return ast_mutex_lock(&iface->mwi_q_lock); case SMDI_MD: return ast_mutex_lock(&iface->md_q_lock); } return -1; } static inline int unlock_msg_q(struct ast_smdi_interface *iface, enum smdi_message_type type) { switch (type) { case SMDI_MWI: return ast_mutex_unlock(&iface->mwi_q_lock); case SMDI_MD: return ast_mutex_unlock(&iface->md_q_lock); } return -1; } static inline void *unlink_from_msg_q(struct ast_smdi_interface *iface, enum smdi_message_type type) { switch (type) { case SMDI_MWI: return ASTOBJ_CONTAINER_UNLINK_START(&iface->mwi_q); case SMDI_MD: return ASTOBJ_CONTAINER_UNLINK_START(&iface->md_q); } return NULL; } static inline struct timeval msg_timestamp(void *msg, enum smdi_message_type type) { struct ast_smdi_md_message *md_msg = msg; struct ast_smdi_mwi_message *mwi_msg = msg; switch (type) { case SMDI_MWI: return mwi_msg->timestamp; case SMDI_MD: return md_msg->timestamp; } return ast_tv(0, 0); } static inline void unref_msg(void *msg, enum smdi_message_type type) { struct ast_smdi_md_message *md_msg = msg; struct ast_smdi_mwi_message *mwi_msg = msg; switch (type) { case SMDI_MWI: ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy); case SMDI_MD: ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy); } } static void purge_old_messages(struct ast_smdi_interface *iface, enum smdi_message_type type) { struct timeval now; long elapsed = 0; void *msg; lock_msg_q(iface, type); msg = unlink_from_msg_q(iface, type); unlock_msg_q(iface, type); /* purge old messages */ now = ast_tvnow(); while (msg) { elapsed = ast_tvdiff_ms(now, msg_timestamp(msg, type)); if (elapsed > iface->msg_expiry) { /* found an expired message */ unref_msg(msg, type); ast_log(LOG_NOTICE, "Purged expired message from %s SMDI %s message queue. " "Message was %ld milliseconds too old.\n", iface->name, (type == SMDI_MD) ? "MD" : "MWI", elapsed - iface->msg_expiry); lock_msg_q(iface, type); msg = unlink_from_msg_q(iface, type); unlock_msg_q(iface, type); } else { /* good message, put it back and return */ switch (type) { case SMDI_MD: ast_smdi_md_message_push(iface, msg); break; case SMDI_MWI: ast_smdi_mwi_message_push(iface, msg); break; } unref_msg(msg, type); break; } } } static void *smdi_msg_pop(struct ast_smdi_interface *iface, enum smdi_message_type type) { void *msg; purge_old_messages(iface, type); lock_msg_q(iface, type); msg = unlink_from_msg_q(iface, type); unlock_msg_q(iface, type); return msg; } enum { OPT_SEARCH_TERMINAL = (1 << 0), OPT_SEARCH_NUMBER = (1 << 1), }; static void *smdi_msg_find(struct ast_smdi_interface *iface, enum smdi_message_type type, const char *search_key, struct ast_flags options) { void *msg = NULL; purge_old_messages(iface, type); switch (type) { case SMDI_MD: if (ast_test_flag(&options, OPT_SEARCH_TERMINAL)) { struct ast_smdi_md_message *md_msg = NULL; /* Searching by the message desk terminal */ ASTOBJ_CONTAINER_TRAVERSE(&iface->md_q, !md_msg, do { if (!strcasecmp(iterator->mesg_desk_term, search_key)) md_msg = ASTOBJ_REF(iterator); } while (0); ); msg = md_msg; } else if (ast_test_flag(&options, OPT_SEARCH_NUMBER)) { struct ast_smdi_md_message *md_msg = NULL; /* Searching by the message desk number */ ASTOBJ_CONTAINER_TRAVERSE(&iface->md_q, !md_msg, do { if (!strcasecmp(iterator->mesg_desk_num, search_key)) md_msg = ASTOBJ_REF(iterator); } while (0); ); msg = md_msg; } else { /* Searching by the forwarding station */ msg = ASTOBJ_CONTAINER_FIND(&iface->md_q, search_key); } break; case SMDI_MWI: msg = ASTOBJ_CONTAINER_FIND(&iface->mwi_q, search_key); break; } return msg; } static void *smdi_message_wait(struct ast_smdi_interface *iface, int timeout, enum smdi_message_type type, const char *search_key, struct ast_flags options) { struct timeval start; long diff = 0; void *msg; ast_cond_t *cond = NULL; ast_mutex_t *lock = NULL; switch (type) { case SMDI_MWI: cond = &iface->mwi_q_cond; lock = &iface->mwi_q_lock; break; case SMDI_MD: cond = &iface->md_q_cond; lock = &iface->md_q_lock; break; } start = ast_tvnow(); while (diff < timeout) { struct timespec ts = { 0, }; struct timeval tv; lock_msg_q(iface, type); if ((msg = smdi_msg_find(iface, type, search_key, options))) { unlock_msg_q(iface, type); return msg; } tv = ast_tvadd(start, ast_tv(0, timeout)); ts.tv_sec = tv.tv_sec; ts.tv_nsec = tv.tv_usec * 1000; /* If there were no messages in the queue, then go to sleep until one * arrives. */ ast_cond_timedwait(cond, lock, &ts); if ((msg = smdi_msg_find(iface, type, search_key, options))) { unlock_msg_q(iface, type); return msg; } unlock_msg_q(iface, type); /* check timeout */ diff = ast_tvdiff_ms(ast_tvnow(), start); } return NULL; } struct ast_smdi_md_message *ast_smdi_md_message_pop(struct ast_smdi_interface *iface) { return smdi_msg_pop(iface, SMDI_MD); } struct ast_smdi_md_message *ast_smdi_md_message_wait(struct ast_smdi_interface *iface, int timeout) { struct ast_flags options = { 0 }; return smdi_message_wait(iface, timeout, SMDI_MD, NULL, options); } struct ast_smdi_mwi_message *ast_smdi_mwi_message_pop(struct ast_smdi_interface *iface) { return smdi_msg_pop(iface, SMDI_MWI); } struct ast_smdi_mwi_message *ast_smdi_mwi_message_wait(struct ast_smdi_interface *iface, int timeout) { struct ast_flags options = { 0 }; return smdi_message_wait(iface, timeout, SMDI_MWI, NULL, options); } struct ast_smdi_mwi_message *ast_smdi_mwi_message_wait_station(struct ast_smdi_interface *iface, int timeout, const char *station) { struct ast_flags options = { 0 }; return smdi_message_wait(iface, timeout, SMDI_MWI, station, options); } struct ast_smdi_interface *ast_smdi_interface_find(const char *iface_name) { return (ASTOBJ_CONTAINER_FIND(&smdi_ifaces, iface_name)); } /*! * \internal * \brief Read an SMDI message. * * \param iface_p the SMDI interface to read from. * * This function loops and reads from and SMDI interface. It must be stopped * using pthread_cancel(). */ static void *smdi_read(void *iface_p) { struct ast_smdi_interface *iface = iface_p; struct ast_smdi_md_message *md_msg; struct ast_smdi_mwi_message *mwi_msg; char c = '\0'; char *cp = NULL; int i; int start = 0; /* read an smdi message */ while ((c = fgetc(iface->file))) { /* check if this is the start of a message */ if (!start) { if (c == 'M') { ast_log(LOG_DEBUG, "Read an 'M' to start an SMDI message\n"); start = 1; } continue; } if (c == 'D') { /* MD message */ start = 0; ast_log(LOG_DEBUG, "Read a 'D' ... it's an MD message.\n"); if (!(md_msg = ast_calloc(1, sizeof(*md_msg)))) { ASTOBJ_UNREF(iface, ast_smdi_interface_destroy); return NULL; } ASTOBJ_INIT(md_msg); /* read the message desk number */ for (i = 0; i < sizeof(md_msg->mesg_desk_num) - 1; i++) { md_msg->mesg_desk_num[i] = fgetc(iface->file); ast_log(LOG_DEBUG, "Read a '%c'\n", md_msg->mesg_desk_num[i]); } md_msg->mesg_desk_num[sizeof(md_msg->mesg_desk_num) - 1] = '\0'; ast_log(LOG_DEBUG, "The message desk number is '%s'\n", md_msg->mesg_desk_num); /* read the message desk terminal number */ for (i = 0; i < sizeof(md_msg->mesg_desk_term) - 1; i++) { md_msg->mesg_desk_term[i] = fgetc(iface->file); ast_log(LOG_DEBUG, "Read a '%c'\n", md_msg->mesg_desk_term[i]); } md_msg->mesg_desk_term[sizeof(md_msg->mesg_desk_term) - 1] = '\0'; ast_log(LOG_DEBUG, "The message desk terminal is '%s'\n", md_msg->mesg_desk_term); /* read the message type */ md_msg->type = fgetc(iface->file); ast_log(LOG_DEBUG, "Message type is '%c'\n", md_msg->type); /* read the forwarding station number (may be blank) */ cp = &md_msg->fwd_st[0]; for (i = 0; i < sizeof(md_msg->fwd_st) - 1; i++) { if ((c = fgetc(iface->file)) == ' ') { *cp = '\0'; ast_log(LOG_DEBUG, "Read a space, done looking for the forwarding station\n"); break; } /* store c in md_msg->fwd_st */ if (i >= iface->msdstrip) { ast_log(LOG_DEBUG, "Read a '%c' and stored it in the forwarding station buffer\n", c); *cp++ = c; } else { ast_log(LOG_DEBUG, "Read a '%c', but didn't store it in the fwd station buffer, because of the msdstrip setting (%d < %d)\n", c, i, iface->msdstrip); } } /* make sure the value is null terminated, even if this truncates it */ md_msg->fwd_st[sizeof(md_msg->fwd_st) - 1] = '\0'; cp = NULL; ast_log(LOG_DEBUG, "The forwarding station is '%s'\n", md_msg->fwd_st); /* Put the fwd_st in the name field so that we can use ASTOBJ_FIND to look * up a message on this field */ ast_copy_string(md_msg->name, md_msg->fwd_st, sizeof(md_msg->name)); /* read the calling station number (may be blank) */ cp = &md_msg->calling_st[0]; for (i = 0; i < sizeof(md_msg->calling_st) - 1; i++) { if (!isdigit((c = fgetc(iface->file)))) { *cp = '\0'; ast_log(LOG_DEBUG, "Read a '%c', but didn't store it in the calling station buffer because it's not a digit\n", c); if (c == ' ') { /* Don't break on a space. We may read the space before the calling station * here if the forwarding station buffer filled up. */ i--; /* We're still on the same character */ continue; } break; } /* store c in md_msg->calling_st */ if (i >= iface->msdstrip) { ast_log(LOG_DEBUG, "Read a '%c' and stored it in the calling station buffer\n", c); *cp++ = c; } else { ast_log(LOG_DEBUG, "Read a '%c', but didn't store it in the calling station buffer, because of the msdstrip setting (%d < %d)\n", c, i, iface->msdstrip); } } /* make sure the value is null terminated, even if this truncates it */ md_msg->calling_st[sizeof(md_msg->calling_st) - 1] = '\0'; cp = NULL; ast_log(LOG_DEBUG, "The calling station is '%s'\n", md_msg->calling_st); /* add the message to the message queue */ md_msg->timestamp = ast_tvnow(); ast_smdi_md_message_push(iface, md_msg); ast_log(LOG_DEBUG, "Recieved SMDI MD message on %s\n", iface->name); ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy); } else if (c == 'W') { /* MWI message */ start = 0; ast_log(LOG_DEBUG, "Read a 'W', it's an MWI message. (No more debug coming for MWI messages)\n"); if (!(mwi_msg = ast_calloc(1, sizeof(*mwi_msg)))) { ASTOBJ_UNREF(iface,ast_smdi_interface_destroy); return NULL; } ASTOBJ_INIT(mwi_msg); /* discard the 'I' (from 'MWI') */ fgetc(iface->file); /* read the forwarding station number (may be blank) */ cp = &mwi_msg->fwd_st[0]; for (i = 0; i < sizeof(mwi_msg->fwd_st) - 1; i++) { if ((c = fgetc(iface->file)) == ' ') { *cp = '\0'; break; } /* store c in md_msg->fwd_st */ if (i >= iface->msdstrip) *cp++ = c; } /* make sure the station number is null terminated, even if this will truncate it */ mwi_msg->fwd_st[sizeof(mwi_msg->fwd_st) - 1] = '\0'; cp = NULL; /* Put the fwd_st in the name field so that we can use ASTOBJ_FIND to look * up a message on this field */ ast_copy_string(mwi_msg->name, mwi_msg->fwd_st, sizeof(mwi_msg->name)); /* read the mwi failure cause */ for (i = 0; i < sizeof(mwi_msg->cause) - 1; i++) mwi_msg->cause[i] = fgetc(iface->file); mwi_msg->cause[sizeof(mwi_msg->cause) - 1] = '\0'; /* add the message to the message queue */ mwi_msg->timestamp = ast_tvnow(); ast_smdi_mwi_message_push(iface, mwi_msg); ast_log(LOG_DEBUG, "Recieved SMDI MWI message on %s\n", iface->name); ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy); } else { ast_log(LOG_ERROR, "Unknown SMDI message type recieved on %s (M%c).\n", iface->name, c); start = 0; } } ast_log(LOG_ERROR, "Error reading from SMDI interface %s, stopping listener thread\n", iface->name); ASTOBJ_UNREF(iface,ast_smdi_interface_destroy); return NULL; } void ast_smdi_md_message_destroy(struct ast_smdi_md_message *msg) { free(msg); } void ast_smdi_mwi_message_destroy(struct ast_smdi_mwi_message *msg) { free(msg); } static void destroy_mailbox_mapping(struct mailbox_mapping *mm) { ast_string_field_free_memory(mm); ASTOBJ_UNREF(mm->iface, ast_smdi_interface_destroy); free(mm); } static void destroy_all_mailbox_mappings(void) { struct mailbox_mapping *mm; ast_mutex_lock(&mwi_monitor.lock); while ((mm = AST_LIST_REMOVE_HEAD(&mwi_monitor.mailbox_mappings, entry))) destroy_mailbox_mapping(mm); ast_mutex_unlock(&mwi_monitor.lock); } static void append_mailbox_mapping(struct ast_variable *var, struct ast_smdi_interface *iface) { struct mailbox_mapping *mm; char *mailbox, *context; if (!(mm = ast_calloc(1, sizeof(*mm)))) return; if (ast_string_field_init(mm, 32)) { free(mm); return; } ast_string_field_set(mm, smdi, var->name); context = ast_strdupa(var->value); mailbox = strsep(&context, "@"); if (ast_strlen_zero(context)) context = "default"; ast_string_field_set(mm, mailbox, mailbox); ast_string_field_set(mm, context, context); mm->iface = ASTOBJ_REF(iface); ast_mutex_lock(&mwi_monitor.lock); AST_LIST_INSERT_TAIL(&mwi_monitor.mailbox_mappings, mm, entry); ast_mutex_unlock(&mwi_monitor.lock); } /*! * \note Called with the mwi_monitor.lock locked */ static void poll_mailbox(struct mailbox_mapping *mm) { char buf[1024]; unsigned int state; snprintf(buf, sizeof(buf), "%s@%s", mm->mailbox, mm->context); state = !!ast_app_has_voicemail(mm->mailbox, NULL); if (state != mm->cur_state) { if (state) ast_smdi_mwi_set(mm->iface, mm->smdi); else ast_smdi_mwi_unset(mm->iface, mm->smdi); mm->cur_state = state; } } static void *mwi_monitor_handler(void *data) { while (!mwi_monitor.stop) { struct timespec ts = { 0, }; struct timeval tv; struct mailbox_mapping *mm; ast_mutex_lock(&mwi_monitor.lock); mwi_monitor.last_poll = ast_tvnow(); AST_LIST_TRAVERSE(&mwi_monitor.mailbox_mappings, mm, entry) poll_mailbox(mm); /* Sleep up to the configured polling interval. Allow unload_module() * to signal us to wake up and exit. */ tv = ast_tvadd(mwi_monitor.last_poll, ast_tv(mwi_monitor.polling_interval, 0)); ts.tv_sec = tv.tv_sec; ts.tv_nsec = tv.tv_usec * 1000; ast_cond_timedwait(&mwi_monitor.cond, &mwi_monitor.lock, &ts); ast_mutex_unlock(&mwi_monitor.lock); } return NULL; } static struct ast_smdi_interface *alloc_smdi_interface(void) { struct ast_smdi_interface *iface; if (!(iface = ast_calloc(1, sizeof(*iface)))) return NULL; ASTOBJ_INIT(iface); ASTOBJ_CONTAINER_INIT(&iface->md_q); ASTOBJ_CONTAINER_INIT(&iface->mwi_q); ast_mutex_init(&iface->md_q_lock); ast_cond_init(&iface->md_q_cond, NULL); ast_mutex_init(&iface->mwi_q_lock); ast_cond_init(&iface->mwi_q_cond, NULL); return iface; } /*! * \internal * \brief Load and reload SMDI configuration. * \param reload this should be 1 if we are reloading and 0 if not. * * This function loads/reloads the SMDI configuration and starts and stops * interfaces accordingly. * * \return zero on success, -1 on failure, and 1 if no smdi interfaces were started. */ static int smdi_load(int reload) { struct ast_config *conf; struct ast_variable *v; struct ast_smdi_interface *iface = NULL; int res = 0; /* Config options */ speed_t baud_rate = B9600; /* 9600 baud rate */ tcflag_t paritybit = PARENB; /* even parity checking */ tcflag_t charsize = CS7; /* seven bit characters */ int stopbits = 0; /* One stop bit */ int msdstrip = 0; /* strip zero digits */ long msg_expiry = SMDI_MSG_EXPIRY_TIME; conf = ast_config_load(config_file); if (!conf) { if (reload) ast_log(LOG_NOTICE, "Unable to reload config %s: SMDI untouched\n", config_file); else ast_log(LOG_NOTICE, "Unable to load config %s: SMDI disabled\n", config_file); return 1; } /* Mark all interfaces that we are listening on. We will unmark them * as we find them in the config file, this way we know any interfaces * still marked after we have finished parsing the config file should * be stopped. */ if (reload) ASTOBJ_CONTAINER_MARKALL(&smdi_ifaces); for (v = ast_variable_browse(conf, "interfaces"); v; v = v->next) { if (!strcasecmp(v->name, "baudrate")) { if (!strcasecmp(v->value, "9600")) baud_rate = B9600; else if (!strcasecmp(v->value, "4800")) baud_rate = B4800; else if (!strcasecmp(v->value, "2400")) baud_rate = B2400; else if (!strcasecmp(v->value, "1200")) baud_rate = B1200; else { ast_log(LOG_NOTICE, "Invalid baud rate '%s' specified in %s (line %d), using default\n", v->value, config_file, v->lineno); baud_rate = B9600; } } else if (!strcasecmp(v->name, "msdstrip")) { if (!sscanf(v->value, "%d", &msdstrip)) { ast_log(LOG_NOTICE, "Invalid msdstrip value in %s (line %d), using default\n", config_file, v->lineno); msdstrip = 0; } else if (0 > msdstrip || msdstrip > 9) { ast_log(LOG_NOTICE, "Invalid msdstrip value in %s (line %d), using default\n", config_file, v->lineno); msdstrip = 0; } } else if (!strcasecmp(v->name, "msgexpirytime")) { if (!sscanf(v->value, "%ld", &msg_expiry)) { ast_log(LOG_NOTICE, "Invalid msgexpirytime value in %s (line %d), using default\n", config_file, v->lineno); msg_expiry = SMDI_MSG_EXPIRY_TIME; } } else if (!strcasecmp(v->name, "paritybit")) { if (!strcasecmp(v->value, "even")) paritybit = PARENB; else if (!strcasecmp(v->value, "odd")) paritybit = PARENB | PARODD; else if (!strcasecmp(v->value, "none")) paritybit = ~PARENB; else { ast_log(LOG_NOTICE, "Invalid parity bit setting in %s (line %d), using default\n", config_file, v->lineno); paritybit = PARENB; } } else if (!strcasecmp(v->name, "charsize")) { if (!strcasecmp(v->value, "7")) charsize = CS7; else if (!strcasecmp(v->value, "8")) charsize = CS8; else { ast_log(LOG_NOTICE, "Invalid character size setting in %s (line %d), using default\n", config_file, v->lineno); charsize = CS7; } } else if (!strcasecmp(v->name, "twostopbits")) { stopbits = ast_true(v->name); } else if (!strcasecmp(v->name, "smdiport")) { if (reload) { /* we are reloading, check if we are already * monitoring this interface, if we are we do * not want to start it again. This also has * the side effect of not updating different * setting for the serial port, but it should * be trivial to rewrite this section so that * options on the port are changed without * restarting the interface. Or the interface * could be restarted with out emptying the * queue. */ if ((iface = ASTOBJ_CONTAINER_FIND(&smdi_ifaces, v->value))) { ast_log(LOG_NOTICE, "SMDI interface %s already running, not restarting\n", iface->name); ASTOBJ_UNMARK(iface); ASTOBJ_UNREF(iface, ast_smdi_interface_destroy); continue; } } if (!(iface = alloc_smdi_interface())) continue; ast_copy_string(iface->name, v->value, sizeof(iface->name)); iface->thread = AST_PTHREADT_NULL; if (!(iface->file = fopen(iface->name, "r"))) { ast_log(LOG_ERROR, "Error opening SMDI interface %s (%s)\n", iface->name, strerror(errno)); ASTOBJ_UNREF(iface, ast_smdi_interface_destroy); continue; } iface->fd = fileno(iface->file); /* Set the proper attributes for our serial port. */ /* get the current attributes from the port */ if (tcgetattr(iface->fd, &iface->mode)) { ast_log(LOG_ERROR, "Error getting atributes of %s (%s)\n", iface->name, strerror(errno)); ASTOBJ_UNREF(iface, ast_smdi_interface_destroy); continue; } /* set the desired speed */ if (cfsetispeed(&iface->mode, baud_rate) || cfsetospeed(&iface->mode, baud_rate)) { ast_log(LOG_ERROR, "Error setting baud rate on %s (%s)\n", iface->name, strerror(errno)); ASTOBJ_UNREF(iface, ast_smdi_interface_destroy); continue; } /* set the stop bits */ if (stopbits) iface->mode.c_cflag = iface->mode.c_cflag | CSTOPB; /* set two stop bits */ else iface->mode.c_cflag = iface->mode.c_cflag & ~CSTOPB; /* set one stop bit */ /* set the parity */ iface->mode.c_cflag = (iface->mode.c_cflag & ~PARENB & ~PARODD) | paritybit; /* set the character size */ iface->mode.c_cflag = (iface->mode.c_cflag & ~CSIZE) | charsize; /* commit the desired attributes */ if (tcsetattr(iface->fd, TCSAFLUSH, &iface->mode)) { ast_log(LOG_ERROR, "Error setting attributes on %s (%s)\n", iface->name, strerror(errno)); ASTOBJ_UNREF(iface, ast_smdi_interface_destroy); continue; } /* set the msdstrip */ iface->msdstrip = msdstrip; /* set the message expiry time */ iface->msg_expiry = msg_expiry; /* start the listener thread */ if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Starting SMDI monitor thread for %s\n", iface->name); if (ast_pthread_create_background(&iface->thread, NULL, smdi_read, iface)) { ast_log(LOG_ERROR, "Error starting SMDI monitor thread for %s\n", iface->name); ASTOBJ_UNREF(iface, ast_smdi_interface_destroy); continue; } ASTOBJ_CONTAINER_LINK(&smdi_ifaces, iface); ASTOBJ_UNREF(iface, ast_smdi_interface_destroy); ast_module_ref(ast_module_info->self); } else { ast_log(LOG_NOTICE, "Ignoring unknown option %s in %s\n", v->name, config_file); } } destroy_all_mailbox_mappings(); mwi_monitor.polling_interval = DEFAULT_POLLING_INTERVAL; iface = NULL; for (v = ast_variable_browse(conf, "mailboxes"); v; v = v->next) { if (!strcasecmp(v->name, "smdiport")) { if (iface) ASTOBJ_UNREF(iface, ast_smdi_interface_destroy); if (!(iface = ASTOBJ_CONTAINER_FIND(&smdi_ifaces, v->value))) { ast_log(LOG_NOTICE, "SMDI interface %s not found\n", iface->name); continue; } } else if (!strcasecmp(v->name, "pollinginterval")) { if (sscanf(v->value, "%u", &mwi_monitor.polling_interval) != 1) { ast_log(LOG_ERROR, "Invalid value for pollinginterval: %s\n", v->value); mwi_monitor.polling_interval = DEFAULT_POLLING_INTERVAL; } } else { if (!iface) { ast_log(LOG_ERROR, "Mailbox mapping ignored, no valid SMDI interface specified in mailboxes section\n"); continue; } append_mailbox_mapping(v, iface); } } if (iface) ASTOBJ_UNREF(iface, ast_smdi_interface_destroy); ast_config_destroy(conf); if (!AST_LIST_EMPTY(&mwi_monitor.mailbox_mappings) && mwi_monitor.thread == AST_PTHREADT_NULL && ast_pthread_create_background(&mwi_monitor.thread, NULL, mwi_monitor_handler, NULL)) { ast_log(LOG_ERROR, "Failed to start MWI monitoring thread. This module will not operate.\n"); return AST_MODULE_LOAD_FAILURE; } /* Prune any interfaces we should no longer monitor. */ if (reload) ASTOBJ_CONTAINER_PRUNE_MARKED(&smdi_ifaces, ast_smdi_interface_destroy); ASTOBJ_CONTAINER_RDLOCK(&smdi_ifaces); /* TODO: this is bad, we need an ASTOBJ method for this! */ if (!smdi_ifaces.head) res = 1; ASTOBJ_CONTAINER_UNLOCK(&smdi_ifaces); return res; } struct smdi_msg_datastore { unsigned int id; struct ast_smdi_interface *iface; struct ast_smdi_md_message *md_msg; }; static void smdi_msg_datastore_destroy(void *data) { struct smdi_msg_datastore *smd = data; if (smd->iface) ASTOBJ_UNREF(smd->iface, ast_smdi_interface_destroy); if (smd->md_msg) ASTOBJ_UNREF(smd->md_msg, ast_smdi_md_message_destroy); free(smd); } static const struct ast_datastore_info smdi_msg_datastore_info = { .type = "SMDIMSG", .destroy = smdi_msg_datastore_destroy, }; static int smdi_msg_id; /*! In milliseconds */ #define SMDI_RETRIEVE_TIMEOUT_DEFAULT 3000 AST_APP_OPTIONS(smdi_msg_ret_options, BEGIN_OPTIONS AST_APP_OPTION('t', OPT_SEARCH_TERMINAL), AST_APP_OPTION('n', OPT_SEARCH_NUMBER), END_OPTIONS ); static int smdi_msg_retrieve_read(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len) { struct ast_module_user *u; AST_DECLARE_APP_ARGS(args, AST_APP_ARG(port); AST_APP_ARG(search_key); AST_APP_ARG(timeout); AST_APP_ARG(options); ); struct ast_flags options = { 0 }; unsigned int timeout = SMDI_RETRIEVE_TIMEOUT_DEFAULT; int res = -1; char *parse = NULL; struct smdi_msg_datastore *smd = NULL; struct ast_datastore *datastore = NULL; struct ast_smdi_interface *iface = NULL; struct ast_smdi_md_message *md_msg = NULL; u = ast_module_user_add(chan); if (ast_strlen_zero(data)) { ast_log(LOG_ERROR, "SMDI_MSG_RETRIEVE requires an argument\n"); goto return_error; } if (!chan) { ast_log(LOG_ERROR, "SMDI_MSG_RETRIEVE must be used with a channel\n"); goto return_error; } ast_autoservice_start(chan); parse = ast_strdupa(data); AST_STANDARD_APP_ARGS(args, parse); if (ast_strlen_zero(args.port) || ast_strlen_zero(args.search_key)) { ast_log(LOG_ERROR, "Invalid arguments provided to SMDI_MSG_RETRIEVE\n"); goto return_error; } if (!(iface = ast_smdi_interface_find(args.port))) { ast_log(LOG_ERROR, "SMDI port '%s' not found\n", args.port); goto return_error; } if (!ast_strlen_zero(args.options)) { ast_app_parse_options(smdi_msg_ret_options, &options, NULL, args.options); } if (!ast_strlen_zero(args.timeout)) { if (sscanf(args.timeout, "%u", &timeout) != 1) { ast_log(LOG_ERROR, "'%s' is not a valid timeout\n", args.timeout); timeout = SMDI_RETRIEVE_TIMEOUT_DEFAULT; } } if (!(md_msg = smdi_message_wait(iface, timeout, SMDI_MD, args.search_key, options))) { ast_log(LOG_WARNING, "No SMDI message retrieved for search key '%s' after " "waiting %u ms.\n", args.search_key, timeout); goto return_error; } if (!(smd = ast_calloc(1, sizeof(*smd)))) goto return_error; smd->iface = ASTOBJ_REF(iface); smd->md_msg = ASTOBJ_REF(md_msg); smd->id = ast_atomic_fetchadd_int((int *) &smdi_msg_id, 1); snprintf(buf, len, "%u", smd->id); if (!(datastore = ast_channel_datastore_alloc(&smdi_msg_datastore_info, buf))) goto return_error; datastore->data = smd; ast_channel_lock(chan); ast_channel_datastore_add(chan, datastore); ast_channel_unlock(chan); res = 0; return_error: if (iface) ASTOBJ_UNREF(iface, ast_smdi_interface_destroy); if (md_msg) ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy); if (smd && !datastore) smdi_msg_datastore_destroy(smd); if (parse) ast_autoservice_stop(chan); ast_module_user_remove(u); return res; } static int smdi_msg_read(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len) { struct ast_module_user *u; int res = -1; AST_DECLARE_APP_ARGS(args, AST_APP_ARG(id); AST_APP_ARG(component); ); char *parse; struct ast_datastore *datastore = NULL; struct smdi_msg_datastore *smd = NULL; u = ast_module_user_add(chan); if (!chan) { ast_log(LOG_ERROR, "SMDI_MSG can not be called without a channel\n"); goto return_error; } if (ast_strlen_zero(data)) { ast_log(LOG_WARNING, "SMDI_MSG requires an argument\n"); goto return_error; } parse = ast_strdupa(data); AST_STANDARD_APP_ARGS(args, parse); if (ast_strlen_zero(args.id)) { ast_log(LOG_WARNING, "ID must be supplied to SMDI_MSG\n"); goto return_error; } if (ast_strlen_zero(args.component)) { ast_log(LOG_WARNING, "ID must be supplied to SMDI_MSG\n"); goto return_error; } ast_channel_lock(chan); datastore = ast_channel_datastore_find(chan, &smdi_msg_datastore_info, args.id); ast_channel_unlock(chan); if (!datastore) { ast_log(LOG_WARNING, "No SMDI message found for message ID '%s'\n", args.id); goto return_error; } smd = datastore->data; if (!strcasecmp(args.component, "number")) { ast_copy_string(buf, smd->md_msg->mesg_desk_num, len); } else if (!strcasecmp(args.component, "terminal")) { ast_copy_string(buf, smd->md_msg->mesg_desk_term, len); } else if (!strcasecmp(args.component, "station")) { ast_copy_string(buf, smd->md_msg->fwd_st, len); } else if (!strcasecmp(args.component, "callerid")) { ast_copy_string(buf, smd->md_msg->calling_st, len); } else if (!strcasecmp(args.component, "type")) { snprintf(buf, len, "%c", smd->md_msg->type); } else { ast_log(LOG_ERROR, "'%s' is not a valid message component for SMDI_MSG\n", args.component); goto return_error; } res = 0; return_error: ast_module_user_remove(u); return 0; } static struct ast_custom_function smdi_msg_retrieve_function = { .name = "SMDI_MSG_RETRIEVE", .synopsis = "Retrieve an SMDI message.", .syntax = "SMDI_MSG_RETRIEVE(,[,timeout[,options]])", .desc = " This function is used to retrieve an incoming SMDI message. It returns\n" "an ID which can be used with the SMDI_MSG() function to access details of\n" "the message. Note that this is a destructive function in the sense that\n" "once an SMDI message is retrieved using this function, it is no longer in\n" "the global SMDI message queue, and can not be accessed by any other Asterisk\n" "channels. The timeout for this function is optional, and the default is\n" "3 seconds. When providing a timeout, it should be in milliseconds.\n" " The default search is done on the forwarding station ID. However, if\n" "you set one of the search key options in the options field, you can change\n" "this behavior.\n" " Options:\n" " t - Instead of searching on the forwarding station, search on the message\n" " desk terminal.\n" " n - Instead of searching on the forwarding station, search on the message\n" " desk number.\n" "", .read = smdi_msg_retrieve_read, }; static struct ast_custom_function smdi_msg_function = { .name = "SMDI_MSG", .synopsis = "Retrieve details about an SMDI message.", .syntax = "SMDI_MSG(,)", .desc = " This function is used to access details of an SMDI message that was\n" "pulled from the incoming SMDI message queue using the SMDI_MSG_RETRIEVE()\n" "function.\n" " Valid message components are:\n" " number - The message desk number\n" " terminal - The message desk terminal\n" " station - The forwarding station\n" " callerid - The callerID of the calling party that was forwarded\n" " type - The call type. The value here is the exact character\n" " that came in on the SMDI link. Typically, example values\n" " are: D - Direct Calls, A - Forward All Calls,\n" " B - Forward Busy Calls, N - Forward No Answer Calls\n" "", .read = smdi_msg_read, }; static int load_module(void) { int res; /* initialize our containers */ memset(&smdi_ifaces, 0, sizeof(smdi_ifaces)); ASTOBJ_CONTAINER_INIT(&smdi_ifaces); ast_mutex_init(&mwi_monitor.lock); ast_cond_init(&mwi_monitor.cond, NULL); ast_custom_function_register(&smdi_msg_retrieve_function); ast_custom_function_register(&smdi_msg_function); /* load the config and start the listener threads*/ res = smdi_load(0); if (res < 0) { return res; } else if (res == 1) { ast_log(LOG_WARNING, "No SMDI interfaces are available to listen on, not starting SMDI listener.\n"); return AST_MODULE_LOAD_DECLINE; } return 0; } static int unload_module(void) { /* this destructor stops any running smdi_read threads */ ASTOBJ_CONTAINER_DESTROYALL(&smdi_ifaces, ast_smdi_interface_destroy); ASTOBJ_CONTAINER_DESTROY(&smdi_ifaces); destroy_all_mailbox_mappings(); ast_mutex_lock(&mwi_monitor.lock); mwi_monitor.stop = 1; ast_cond_signal(&mwi_monitor.cond); ast_mutex_unlock(&mwi_monitor.lock); if (mwi_monitor.thread != AST_PTHREADT_NULL) { pthread_join(mwi_monitor.thread, NULL); } ast_custom_function_unregister(&smdi_msg_retrieve_function); ast_custom_function_unregister(&smdi_msg_function); return 0; } static int reload(void) { int res; res = smdi_load(1); if (res < 0) { return res; } else if (res == 1) { ast_log(LOG_WARNING, "No SMDI interfaces were specified to listen on, not starting SDMI listener.\n"); return 0; } else return 0; } AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Simplified Message Desk Interface (SMDI) Resource", .load = load_module, .unload = unload_module, .reload = reload, ); asterisk-1.4.21.2/res/res_indications.c0000644000175000017500000002641210522474204017145 0ustar maniacmaniac/* * Asterisk -- An open source telephony toolkit. * * Copyright (C) 2002, Pauline Middelink * * * 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 res_indications.c * * \brief Load the indications * * \author Pauline Middelink * * Load the country specific dialtones into the asterisk PBX. */ #include "asterisk.h" ASTERISK_FILE_VERSION(__FILE__, "$Revision: 47051 $") #include #include #include #include #include #include #include #include #include "asterisk/lock.h" #include "asterisk/file.h" #include "asterisk/cli.h" #include "asterisk/logger.h" #include "asterisk/config.h" #include "asterisk/channel.h" #include "asterisk/pbx.h" #include "asterisk/module.h" #include "asterisk/translate.h" #include "asterisk/indications.h" #include "asterisk/utils.h" /* Globals */ static const char config[] = "indications.conf"; /* * Help for commands provided by this module ... */ static char help_add_indication[] = "Usage: indication add \"\"\n" " Add the given indication to the country.\n"; static char help_remove_indication[] = "Usage: indication remove \n" " Remove the given indication from the country.\n"; static char help_show_indications[] = "Usage: indication show [ ...]\n" " Display either a condensed for of all country/indications, or the\n" " indications for the specified countries.\n"; char *playtones_desc= "PlayTones(arg): Plays a tone list. Execution will continue with the next step immediately,\n" "while the tones continue to play.\n" "Arg is either the tone name defined in the indications.conf configuration file, or a directly\n" "specified list of frequencies and durations.\n" "See the sample indications.conf for a description of the specification of a tonelist.\n\n" "Use the StopPlayTones application to stop the tones playing. \n"; /* * Implementation of functions provided by this module */ /* * ADD INDICATION command stuff */ static int handle_add_indication(int fd, int argc, char *argv[]) { struct tone_zone *tz; int created_country = 0; if (argc != 5) return RESULT_SHOWUSAGE; tz = ast_get_indication_zone(argv[2]); if (!tz) { /* country does not exist, create it */ ast_log(LOG_NOTICE, "Country '%s' does not exist, creating it.\n",argv[2]); if (!(tz = ast_calloc(1, sizeof(*tz)))) { return -1; } ast_copy_string(tz->country,argv[2],sizeof(tz->country)); if (ast_register_indication_country(tz)) { ast_log(LOG_WARNING, "Unable to register new country\n"); free(tz); return -1; } created_country = 1; } if (ast_register_indication(tz,argv[3],argv[4])) { ast_log(LOG_WARNING, "Unable to register indication %s/%s\n",argv[2],argv[3]); if (created_country) ast_unregister_indication_country(argv[2]); return -1; } return 0; } /* * REMOVE INDICATION command stuff */ static int handle_remove_indication(int fd, int argc, char *argv[]) { struct tone_zone *tz; if (argc != 3 && argc != 4) return RESULT_SHOWUSAGE; if (argc == 3) { /* remove entiry country */ if (ast_unregister_indication_country(argv[2])) { ast_log(LOG_WARNING, "Unable to unregister indication country %s\n",argv[2]); return -1; } return 0; } tz = ast_get_indication_zone(argv[2]); if (!tz) { ast_log(LOG_WARNING, "Unable to unregister indication %s/%s, country does not exists\n",argv[2],argv[3]); return -1; } if (ast_unregister_indication(tz,argv[3])) { ast_log(LOG_WARNING, "Unable to unregister indication %s/%s\n",argv[2],argv[3]); return -1; } return 0; } /* * SHOW INDICATIONS command stuff */ static int handle_show_indications(int fd, int argc, char *argv[]) { struct tone_zone *tz = NULL; char buf[256]; int found_country = 0; if (argc == 2) { /* no arguments, show a list of countries */ ast_cli(fd,"Country Alias Description\n" "===========================\n"); while ( (tz = ast_walk_indications(tz) ) ) ast_cli(fd,"%-7.7s %-7.7s %s\n", tz->country, tz->alias, tz->description); return 0; } /* there was a request for specific country(ies), lets humor them */ while ( (tz = ast_walk_indications(tz) ) ) { int i,j; for (i=2; icountry,argv[i])==0 && !tz->alias[0]) { struct tone_zone_sound* ts; if (!found_country) { found_country = 1; ast_cli(fd,"Country Indication PlayList\n" "=====================================\n"); } j = snprintf(buf,sizeof(buf),"%-7.7s %-15.15s ",tz->country,""); for (i=0; inrringcadence; i++) { j += snprintf(buf+j,sizeof(buf)-j,"%d,",tz->ringcadence[i]); } if (tz->nrringcadence) j--; ast_copy_string(buf+j,"\n",sizeof(buf)-j); ast_cli(fd,buf); for (ts=tz->tones; ts; ts=ts->next) ast_cli(fd,"%-7.7s %-15.15s %s\n",tz->country,ts->name,ts->data); break; } } } if (!found_country) ast_cli(fd,"No countries matched your criteria.\n"); return -1; } /* * Playtones command stuff */ static int handle_playtones(struct ast_channel *chan, void *data) { struct tone_zone_sound *ts; int res; if (!data || !((char*)data)[0]) { ast_log(LOG_NOTICE,"Nothing to play\n"); return -1; } ts = ast_get_indication_tone(chan->zone, (const char*)data); if (ts && ts->data[0]) res = ast_playtones_start(chan, 0, ts->data, 0); else res = ast_playtones_start(chan, 0, (const char*)data, 0); if (res) ast_log(LOG_NOTICE,"Unable to start playtones\n"); return res; } /* * StopPlaylist command stuff */ static int handle_stopplaytones(struct ast_channel *chan, void *data) { ast_playtones_stop(chan); return 0; } /* * Load module stuff */ static int ind_load_module(void) { struct ast_config *cfg; struct ast_variable *v; char *cxt; char *c; struct tone_zone *tones; const char *country = NULL; /* that the following cast is needed, is yuk! */ /* yup, checked it out. It is NOT written to. */ cfg = ast_config_load((char *)config); if (!cfg) return -1; /* Use existing config to populate the Indication table */ cxt = ast_category_browse(cfg, NULL); while(cxt) { /* All categories but "general" are considered countries */ if (!strcasecmp(cxt, "general")) { cxt = ast_category_browse(cfg, cxt); continue; } if (!(tones = ast_calloc(1, sizeof(*tones)))) { ast_config_destroy(cfg); return -1; } ast_copy_string(tones->country,cxt,sizeof(tones->country)); v = ast_variable_browse(cfg, cxt); while(v) { if (!strcasecmp(v->name, "description")) { ast_copy_string(tones->description, v->value, sizeof(tones->description)); } else if ((!strcasecmp(v->name,"ringcadence"))||(!strcasecmp(v->name,"ringcadance"))) { char *ring,*rings = ast_strdupa(v->value); c = rings; ring = strsep(&c,","); while (ring) { int *tmp, val; if (!isdigit(ring[0]) || (val=atoi(ring))==-1) { ast_log(LOG_WARNING,"Invalid ringcadence given '%s' at line %d.\n",ring,v->lineno); ring = strsep(&c,","); continue; } if (!(tmp = ast_realloc(tones->ringcadence, (tones->nrringcadence + 1) * sizeof(int)))) { ast_config_destroy(cfg); return -1; } tones->ringcadence = tmp; tmp[tones->nrringcadence] = val; tones->nrringcadence++; /* next item */ ring = strsep(&c,","); } } else if (!strcasecmp(v->name,"alias")) { char *countries = ast_strdupa(v->value); c = countries; country = strsep(&c,","); while (country) { struct tone_zone* azone; if (!(azone = ast_calloc(1, sizeof(*azone)))) { ast_config_destroy(cfg); return -1; } ast_copy_string(azone->country, country, sizeof(azone->country)); ast_copy_string(azone->alias, cxt, sizeof(azone->alias)); if (ast_register_indication_country(azone)) { ast_log(LOG_WARNING, "Unable to register indication alias at line %d.\n",v->lineno); free(tones); } /* next item */ country = strsep(&c,","); } } else { /* add tone to country */ struct tone_zone_sound *ps,*ts; for (ps=NULL,ts=tones->tones; ts; ps=ts, ts=ts->next) { if (strcasecmp(v->name,ts->name)==0) { /* already there */ ast_log(LOG_NOTICE,"Duplicate entry '%s', skipped.\n",v->name); goto out; } } /* not there, add it to the back */ if (!(ts = ast_malloc(sizeof(*ts)))) { ast_config_destroy(cfg); return -1; } ts->next = NULL; ts->name = strdup(v->name); ts->data = strdup(v->value); if (ps) ps->next = ts; else tones->tones = ts; } out: v = v->next; } if (tones->description[0] || tones->alias[0] || tones->tones) { if (ast_register_indication_country(tones)) { ast_log(LOG_WARNING, "Unable to register indication at line %d.\n",v->lineno); free(tones); } } else free(tones); cxt = ast_category_browse(cfg, cxt); } /* determine which country is the default */ country = ast_variable_retrieve(cfg,"general","country"); if (!country || !*country || ast_set_indication_country(country)) ast_log(LOG_WARNING,"Unable to set the default country (for indication tones)\n"); ast_config_destroy(cfg); return 0; } /* * CLI entries for commands provided by this module */ static struct ast_cli_entry cli_show_indications_deprecated = { { "show", "indications", NULL }, handle_show_indications, NULL, NULL }; static struct ast_cli_entry cli_indications[] = { { { "indication", "add", NULL }, handle_add_indication, "Add the given indication to the country", help_add_indication, NULL }, { { "indication", "remove", NULL }, handle_remove_indication, "Remove the given indication from the country", help_remove_indication, NULL }, { { "indication", "show", NULL }, handle_show_indications, "Display a list of all countries/indications", help_show_indications, NULL, &cli_show_indications_deprecated }, }; /* * Standard module functions ... */ static int unload_module(void) { /* remove the registed indications... */ ast_unregister_indication_country(NULL); /* and the functions */ ast_cli_unregister_multiple(cli_indications, sizeof(cli_indications) / sizeof(struct ast_cli_entry)); ast_unregister_application("PlayTones"); ast_unregister_application("StopPlayTones"); return 0; } static int load_module(void) { if (ind_load_module()) return AST_MODULE_LOAD_DECLINE; ast_cli_register_multiple(cli_indications, sizeof(cli_indications) / sizeof(struct ast_cli_entry)); ast_register_application("PlayTones", handle_playtones, "Play a tone list", playtones_desc); ast_register_application("StopPlayTones", handle_stopplaytones, "Stop playing a tone list","Stop playing a tone list"); return 0; } static int reload(void) { /* remove the registed indications... */ ast_unregister_indication_country(NULL); return ind_load_module(); } AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Indications Resource", .load = load_module, .unload = unload_module, .reload = reload, ); asterisk-1.4.21.2/res/res_musiconhold.c0000644000175000017500000010626411010131342017153 0ustar maniacmaniac/* * Asterisk -- An open source telephony toolkit. * * Copyright (C) 1999 - 2006, Digium, Inc. * * Mark Spencer * * 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 Routines implementing music on hold * * \arg See also \ref Config_moh * * \author Mark Spencer */ /*** MODULEINFO win32 zaptel ***/ #include "asterisk.h" ASTERISK_FILE_VERSION(__FILE__, "$Revision: 115418 $") #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef SOLARIS #include #endif #ifdef HAVE_ZAPTEL #include #endif #include "asterisk/lock.h" #include "asterisk/file.h" #include "asterisk/logger.h" #include "asterisk/channel.h" #include "asterisk/pbx.h" #include "asterisk/options.h" #include "asterisk/module.h" #include "asterisk/translate.h" #include "asterisk/say.h" #include "asterisk/musiconhold.h" #include "asterisk/config.h" #include "asterisk/utils.h" #include "asterisk/cli.h" #include "asterisk/stringfields.h" #include "asterisk/linkedlists.h" #define INITIAL_NUM_FILES 8 static char *app0 = "MusicOnHold"; static char *app1 = "WaitMusicOnHold"; static char *app2 = "SetMusicOnHold"; static char *app3 = "StartMusicOnHold"; static char *app4 = "StopMusicOnHold"; static char *synopsis0 = "Play Music On Hold indefinitely"; static char *synopsis1 = "Wait, playing Music On Hold"; static char *synopsis2 = "Set default Music On Hold class"; static char *synopsis3 = "Play Music On Hold"; static char *synopsis4 = "Stop Playing Music On Hold"; static char *descrip0 = "MusicOnHold(class): " "Plays hold music specified by class. If omitted, the default\n" "music source for the channel will be used. Set the default \n" "class with the SetMusicOnHold() application.\n" "Returns -1 on hangup.\n" "Never returns otherwise.\n"; static char *descrip1 = "WaitMusicOnHold(delay): " "Plays hold music specified number of seconds. Returns 0 when\n" "done, or -1 on hangup. If no hold music is available, the delay will\n" "still occur with no sound.\n"; static char *descrip2 = "SetMusicOnHold(class): " "Sets the default class for music on hold for a given channel. When\n" "music on hold is activated, this class will be used to select which\n" "music is played.\n"; static char *descrip3 = "StartMusicOnHold(class): " "Starts playing music on hold, uses default music class for channel.\n" "Starts playing music specified by class. If omitted, the default\n" "music source for the channel will be used. Always returns 0.\n"; static char *descrip4 = "StopMusicOnHold: " "Stops playing music on hold.\n"; static int respawn_time = 20; struct moh_files_state { struct mohclass *class; int origwfmt; int samples; int sample_queue; int pos; int save_pos; char *save_pos_filename; }; #define MOH_QUIET (1 << 0) #define MOH_SINGLE (1 << 1) #define MOH_CUSTOM (1 << 2) #define MOH_RANDOMIZE (1 << 3) struct mohclass { char name[MAX_MUSICCLASS]; char dir[256]; char args[256]; char mode[80]; /*! A dynamically sized array to hold the list of filenames in "files" mode */ char **filearray; /*! The current size of the filearray */ int allowed_files; /*! The current number of files loaded into the filearray */ int total_files; unsigned int flags; /*! The format from the MOH source, not applicable to "files" mode */ int format; /*! The pid of the external application delivering MOH */ int pid; time_t start; pthread_t thread; /*! Source of audio */ int srcfd; /*! FD for timing source */ int pseudofd; /*! Number of users */ int inuse; unsigned int delete:1; AST_LIST_HEAD_NOLOCK(, mohdata) members; AST_LIST_ENTRY(mohclass) list; }; struct mohdata { int pipe[2]; int origwfmt; struct mohclass *parent; struct ast_frame f; AST_LIST_ENTRY(mohdata) list; }; AST_LIST_HEAD_STATIC(mohclasses, mohclass); #define LOCAL_MPG_123 "/usr/local/bin/mpg123" #define MPG_123 "/usr/bin/mpg123" #define MAX_MP3S 256 static int ast_moh_destroy_one(struct mohclass *moh); static int reload(void); static void ast_moh_free_class(struct mohclass **mohclass) { struct mohdata *member; struct mohclass *class = *mohclass; int i; while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) free(member); if (class->thread) { pthread_cancel(class->thread); class->thread = 0; } if (class->filearray) { for (i = 0; i < class->total_files; i++) free(class->filearray[i]); free(class->filearray); } free(class); *mohclass = NULL; } static void moh_files_release(struct ast_channel *chan, void *data) { struct moh_files_state *state; if (chan) { if ((state = chan->music_state)) { if (chan->stream) { ast_closestream(chan->stream); chan->stream = NULL; } if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name); if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) { ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%d'\n", chan->name, state->origwfmt); } state->save_pos = state->pos; if (ast_atomic_dec_and_test(&state->class->inuse) && state->class->delete) ast_moh_destroy_one(state->class); } } } static int ast_moh_files_next(struct ast_channel *chan) { struct moh_files_state *state = chan->music_state; int tries; /* Discontinue a stream if it is running already */ if (chan->stream) { ast_closestream(chan->stream); chan->stream = NULL; } if (!state->class->total_files) { ast_log(LOG_WARNING, "No files available for class '%s'\n", state->class->name); return -1; } /* If a specific file has been saved confirm it still exists and that it is still valid */ if (state->save_pos >= 0 && state->save_pos < state->class->total_files && state->class->filearray[state->save_pos] == state->save_pos_filename) { state->pos = state->save_pos; state->save_pos = -1; } else if (ast_test_flag(state->class, MOH_RANDOMIZE)) { /* Get a random file and ensure we can open it */ for (tries = 0; tries < 20; tries++) { state->pos = ast_random() % state->class->total_files; if (ast_fileexists(state->class->filearray[state->pos], NULL, NULL) > 0) break; } state->save_pos = -1; state->samples = 0; } else { /* This is easy, just increment our position and make sure we don't exceed the total file count */ state->pos++; state->pos %= state->class->total_files; state->save_pos = -1; state->samples = 0; } if (!ast_openstream_full(chan, state->class->filearray[state->pos], chan->language, 1)) { ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno)); state->pos++; state->pos %= state->class->total_files; return -1; } /* Record the pointer to the filename for position resuming later */ state->save_pos_filename = state->class->filearray[state->pos]; if (option_debug) ast_log(LOG_DEBUG, "%s Opened file %d '%s'\n", chan->name, state->pos, state->class->filearray[state->pos]); if (state->samples) ast_seekstream(chan->stream, state->samples, SEEK_SET); return 0; } static struct ast_frame *moh_files_readframe(struct ast_channel *chan) { struct ast_frame *f = NULL; if (!(chan->stream && (f = ast_readframe(chan->stream)))) { if (!ast_moh_files_next(chan)) f = ast_readframe(chan->stream); } return f; } static int moh_files_generator(struct ast_channel *chan, void *data, int len, int samples) { struct moh_files_state *state = chan->music_state; struct ast_frame *f = NULL; int res = 0; state->sample_queue += samples; while (state->sample_queue > 0) { if ((f = moh_files_readframe(chan))) { state->samples += f->samples; state->sample_queue -= f->samples; res = ast_write(chan, f); ast_frfree(f); if (res < 0) { ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno)); return -1; } } else return -1; } return res; } static void *moh_files_alloc(struct ast_channel *chan, void *params) { struct moh_files_state *state; struct mohclass *class = params; if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) { chan->music_state = state; state->class = class; state->save_pos = -1; } else state = chan->music_state; if (state) { if (state->class != class) { /* initialize */ memset(state, 0, sizeof(*state)); state->class = class; if (ast_test_flag(state->class, MOH_RANDOMIZE) && class->total_files) state->pos = ast_random() % class->total_files; } state->origwfmt = chan->writeformat; if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on %s\n", class->name, chan->name); } return chan->music_state; } static struct ast_generator moh_file_stream = { alloc: moh_files_alloc, release: moh_files_release, generate: moh_files_generator, }; static int spawn_mp3(struct mohclass *class) { int fds[2]; int files = 0; char fns[MAX_MP3S][80]; char *argv[MAX_MP3S + 50]; char xargs[256]; char *argptr; int argc = 0; DIR *dir = NULL; struct dirent *de; sigset_t signal_set, old_set; if (!strcasecmp(class->dir, "nodir")) { files = 1; } else { dir = opendir(class->dir); if (!dir && !strstr(class->dir,"http://") && !strstr(class->dir,"HTTP://")) { ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir); return -1; } } if (!ast_test_flag(class, MOH_CUSTOM)) { argv[argc++] = "mpg123"; argv[argc++] = "-q"; argv[argc++] = "-s"; argv[argc++] = "--mono"; argv[argc++] = "-r"; argv[argc++] = "8000"; if (!ast_test_flag(class, MOH_SINGLE)) { argv[argc++] = "-b"; argv[argc++] = "2048"; } argv[argc++] = "-f"; if (ast_test_flag(class, MOH_QUIET)) argv[argc++] = "4096"; else argv[argc++] = "8192"; /* Look for extra arguments and add them to the list */ ast_copy_string(xargs, class->args, sizeof(xargs)); argptr = xargs; while (!ast_strlen_zero(argptr)) { argv[argc++] = argptr; strsep(&argptr, ","); } } else { /* Format arguments for argv vector */ ast_copy_string(xargs, class->args, sizeof(xargs)); argptr = xargs; while (!ast_strlen_zero(argptr)) { argv[argc++] = argptr; strsep(&argptr, " "); } } if (strstr(class->dir,"http://") || strstr(class->dir,"HTTP://")) { ast_copy_string(fns[files], class->dir, sizeof(fns[files])); argv[argc++] = fns[files]; files++; } else if (dir) { while ((de = readdir(dir)) && (files < MAX_MP3S)) { if ((strlen(de->d_name) > 3) && ((ast_test_flag(class, MOH_CUSTOM) && (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") || !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) || !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) { ast_copy_string(fns[files], de->d_name, sizeof(fns[files])); argv[argc++] = fns[files]; files++; } } } argv[argc] = NULL; if (dir) { closedir(dir); } if (pipe(fds)) { ast_log(LOG_WARNING, "Pipe failed\n"); return -1; } if (!files) { ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir); close(fds[0]); close(fds[1]); return -1; } if (time(NULL) - class->start < respawn_time) { sleep(respawn_time - (time(NULL) - class->start)); } /* Block signals during the fork() */ sigfillset(&signal_set); pthread_sigmask(SIG_BLOCK, &signal_set, &old_set); time(&class->start); class->pid = fork(); if (class->pid < 0) { close(fds[0]); close(fds[1]); ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno)); return -1; } if (!class->pid) { int x; if (ast_opt_high_priority) ast_set_priority(0); /* Reset ignored signals back to default */ signal(SIGPIPE, SIG_DFL); pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL); close(fds[0]); /* Stdout goes to pipe */ dup2(fds[1], STDOUT_FILENO); /* Close unused file descriptors */ for (x=3;x<8192;x++) { if (-1 != fcntl(x, F_GETFL)) { close(x); } } /* Child */ chdir(class->dir); if (ast_test_flag(class, MOH_CUSTOM)) { execv(argv[0], argv); } else { /* Default install is /usr/local/bin */ execv(LOCAL_MPG_123, argv); /* Many places have it in /usr/bin */ execv(MPG_123, argv); /* Check PATH as a last-ditch effort */ execvp("mpg123", argv); } ast_log(LOG_WARNING, "Exec failed: %s\n", strerror(errno)); close(fds[1]); _exit(1); } else { /* Parent */ pthread_sigmask(SIG_SETMASK, &old_set, NULL); close(fds[1]); } return fds[0]; } static void *monmp3thread(void *data) { #define MOH_MS_INTERVAL 100 struct mohclass *class = data; struct mohdata *moh; char buf[8192]; short sbuf[8192]; int res, res2; int len; struct timeval tv, tv_tmp; tv.tv_sec = 0; tv.tv_usec = 0; for(;/* ever */;) { pthread_testcancel(); /* Spawn mp3 player if it's not there */ if (class->srcfd < 0) { if ((class->srcfd = spawn_mp3(class)) < 0) { ast_log(LOG_WARNING, "Unable to spawn mp3player\n"); /* Try again later */ sleep(500); pthread_testcancel(); } } if (class->pseudofd > -1) { #ifdef SOLARIS thr_yield(); #endif /* Pause some amount of time */ res = read(class->pseudofd, buf, sizeof(buf)); pthread_testcancel(); } else { long delta; /* Reliable sleep */ tv_tmp = ast_tvnow(); if (ast_tvzero(tv)) tv = tv_tmp; delta = ast_tvdiff_ms(tv_tmp, tv); if (delta < MOH_MS_INTERVAL) { /* too early */ tv = ast_tvadd(tv, ast_samp2tv(MOH_MS_INTERVAL, 1000)); /* next deadline */ usleep(1000 * (MOH_MS_INTERVAL - delta)); pthread_testcancel(); } else { ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n"); tv = tv_tmp; } res = 8 * MOH_MS_INTERVAL; /* 8 samples per millisecond */ } if (AST_LIST_EMPTY(&class->members)) continue; /* Read mp3 audio */ len = ast_codec_get_len(class->format, res); if ((res2 = read(class->srcfd, sbuf, len)) != len) { if (!res2) { close(class->srcfd); class->srcfd = -1; pthread_testcancel(); if (class->pid > 1) { kill(class->pid, SIGHUP); usleep(100000); kill(class->pid, SIGTERM); usleep(100000); kill(class->pid, SIGKILL); class->pid = 0; } } else ast_log(LOG_DEBUG, "Read %d bytes of audio while expecting %d\n", res2, len); continue; } pthread_testcancel(); AST_LIST_LOCK(&mohclasses); AST_LIST_TRAVERSE(&class->members, moh, list) { /* Write data */ if ((res = write(moh->pipe[1], sbuf, res2)) != res2) { if (option_debug) ast_log(LOG_DEBUG, "Only wrote %d of %d bytes to pipe\n", res, res2); } } AST_LIST_UNLOCK(&mohclasses); } return NULL; } static int moh0_exec(struct ast_channel *chan, void *data) { if (ast_moh_start(chan, data, NULL)) { ast_log(LOG_WARNING, "Unable to start music on hold (class '%s') on channel %s\n", (char *)data, chan->name); return 0; } while (!ast_safe_sleep(chan, 10000)); ast_moh_stop(chan); return -1; } static int moh1_exec(struct ast_channel *chan, void *data) { int res; if (!data || !atoi(data)) { ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n"); return -1; } if (ast_moh_start(chan, NULL, NULL)) { ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), chan->name); return 0; } res = ast_safe_sleep(chan, atoi(data) * 1000); ast_moh_stop(chan); return res; } static int moh2_exec(struct ast_channel *chan, void *data) { if (ast_strlen_zero(data)) { ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n"); return -1; } ast_string_field_set(chan, musicclass, data); return 0; } static int moh3_exec(struct ast_channel *chan, void *data) { char *class = NULL; if (data && strlen(data)) class = data; if (ast_moh_start(chan, class, NULL)) ast_log(LOG_NOTICE, "Unable to start music on hold class '%s' on channel %s\n", class ? class : "default", chan->name); return 0; } static int moh4_exec(struct ast_channel *chan, void *data) { ast_moh_stop(chan); return 0; } /*! \note This function should be called with the mohclasses list locked */ static struct mohclass *get_mohbyname(const char *name, int warn) { struct mohclass *moh = NULL; AST_LIST_TRAVERSE(&mohclasses, moh, list) { if (!strcasecmp(name, moh->name)) break; } if (!moh && warn) ast_log(LOG_WARNING, "Music on Hold class '%s' not found\n", name); return moh; } static struct mohdata *mohalloc(struct mohclass *cl) { struct mohdata *moh; long flags; if (!(moh = ast_calloc(1, sizeof(*moh)))) return NULL; if (pipe(moh->pipe)) { ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno)); free(moh); return NULL; } /* Make entirely non-blocking */ flags = fcntl(moh->pipe[0], F_GETFL); fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK); flags = fcntl(moh->pipe[1], F_GETFL); fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK); moh->f.frametype = AST_FRAME_VOICE; moh->f.subclass = cl->format; moh->f.offset = AST_FRIENDLY_OFFSET; moh->parent = cl; AST_LIST_LOCK(&mohclasses); AST_LIST_INSERT_HEAD(&cl->members, moh, list); AST_LIST_UNLOCK(&mohclasses); return moh; } static void moh_release(struct ast_channel *chan, void *data) { struct mohdata *moh = data; int oldwfmt; AST_LIST_LOCK(&mohclasses); AST_LIST_REMOVE(&moh->parent->members, moh, list); AST_LIST_UNLOCK(&mohclasses); close(moh->pipe[0]); close(moh->pipe[1]); oldwfmt = moh->origwfmt; if (moh->parent->delete && ast_atomic_dec_and_test(&moh->parent->inuse)) ast_moh_destroy_one(moh->parent); free(moh); if (chan) { if (oldwfmt && ast_set_write_format(chan, oldwfmt)) ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(oldwfmt)); if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name); } } static void *moh_alloc(struct ast_channel *chan, void *params) { struct mohdata *res; struct mohclass *class = params; if ((res = mohalloc(class))) { res->origwfmt = chan->writeformat; if (ast_set_write_format(chan, class->format)) { ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format)); moh_release(NULL, res); res = NULL; } if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name); } return res; } static int moh_generate(struct ast_channel *chan, void *data, int len, int samples) { struct mohdata *moh = data; short buf[1280 + AST_FRIENDLY_OFFSET / 2]; int res; if (!moh->parent->pid) return -1; len = ast_codec_get_len(moh->parent->format, samples); if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) { ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name); len = sizeof(buf) - AST_FRIENDLY_OFFSET; } res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len); if (res <= 0) return 0; moh->f.datalen = res; moh->f.data = buf + AST_FRIENDLY_OFFSET / 2; moh->f.samples = ast_codec_get_samples(&moh->f); if (ast_write(chan, &moh->f) < 0) { ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno)); return -1; } return 0; } static struct ast_generator mohgen = { alloc: moh_alloc, release: moh_release, generate: moh_generate, }; static int moh_add_file(struct mohclass *class, const char *filepath) { if (!class->allowed_files) { if (!(class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray)))) return -1; class->allowed_files = INITIAL_NUM_FILES; } else if (class->total_files == class->allowed_files) { if (!(class->filearray = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2))) { class->allowed_files = 0; class->total_files = 0; return -1; } class->allowed_files *= 2; } if (!(class->filearray[class->total_files] = ast_strdup(filepath))) return -1; class->total_files++; return 0; } static int moh_scan_files(struct mohclass *class) { DIR *files_DIR; struct dirent *files_dirent; char path[PATH_MAX]; char filepath[PATH_MAX]; char *ext; struct stat statbuf; int dirnamelen; int i; files_DIR = opendir(class->dir); if (!files_DIR) { ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist\n", class->dir); return -1; } for (i = 0; i < class->total_files; i++) free(class->filearray[i]); class->total_files = 0; dirnamelen = strlen(class->dir) + 2; getcwd(path, sizeof(path)); chdir(class->dir); while ((files_dirent = readdir(files_DIR))) { /* The file name must be at least long enough to have the file type extension */ if ((strlen(files_dirent->d_name) < 4)) continue; /* Skip files that starts with a dot */ if (files_dirent->d_name[0] == '.') continue; /* Skip files without extensions... they are not audio */ if (!strchr(files_dirent->d_name, '.')) continue; snprintf(filepath, sizeof(filepath), "%s/%s", class->dir, files_dirent->d_name); if (stat(filepath, &statbuf)) continue; if (!S_ISREG(statbuf.st_mode)) continue; if ((ext = strrchr(filepath, '.'))) { *ext = '\0'; ext++; } /* if the file is present in multiple formats, ensure we only put it into the list once */ for (i = 0; i < class->total_files; i++) if (!strcmp(filepath, class->filearray[i])) break; if (i == class->total_files) { if (moh_add_file(class, filepath)) break; } } closedir(files_DIR); chdir(path); return class->total_files; } static int moh_register(struct mohclass *moh, int reload) { #ifdef HAVE_ZAPTEL int x; #endif struct mohclass *mohclass = NULL; int res = 0; AST_LIST_LOCK(&mohclasses); if ((mohclass = get_mohbyname(moh->name, 0))) { if (!mohclass->delete) { ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name); free(moh); AST_LIST_UNLOCK(&mohclasses); return -1; } } AST_LIST_UNLOCK(&mohclasses); time(&moh->start); moh->start -= respawn_time; if (!strcasecmp(moh->mode, "files")) { res = moh_scan_files(moh); if (res <= 0) { if (res == 0) { if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Files not found in %s for moh class:%s\n", moh->dir, moh->name); } ast_moh_free_class(&moh); return -1; } if (strchr(moh->args, 'r')) ast_set_flag(moh, MOH_RANDOMIZE); } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") || !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") || !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) { if (!strcasecmp(moh->mode, "custom")) ast_set_flag(moh, MOH_CUSTOM); else if (!strcasecmp(moh->mode, "mp3nb")) ast_set_flag(moh, MOH_SINGLE); else if (!strcasecmp(moh->mode, "quietmp3nb")) ast_set_flag(moh, MOH_SINGLE | MOH_QUIET); else if (!strcasecmp(moh->mode, "quietmp3")) ast_set_flag(moh, MOH_QUIET); moh->srcfd = -1; #ifdef HAVE_ZAPTEL /* Open /dev/zap/pseudo for timing... Is there a better, yet reliable way to do this? */ moh->pseudofd = open("/dev/zap/pseudo", O_RDONLY); if (moh->pseudofd < 0) { ast_log(LOG_WARNING, "Unable to open pseudo channel for timing... Sound may be choppy.\n"); } else { x = 320; ioctl(moh->pseudofd, ZT_SET_BLOCKSIZE, &x); } #else moh->pseudofd = -1; #endif if (ast_pthread_create_background(&moh->thread, NULL, monmp3thread, moh)) { ast_log(LOG_WARNING, "Unable to create moh...\n"); if (moh->pseudofd > -1) close(moh->pseudofd); ast_moh_free_class(&moh); return -1; } } else { ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode); ast_moh_free_class(&moh); return -1; } AST_LIST_LOCK(&mohclasses); AST_LIST_INSERT_HEAD(&mohclasses, moh, list); AST_LIST_UNLOCK(&mohclasses); return 0; } static void local_ast_moh_cleanup(struct ast_channel *chan) { if (chan->music_state) { free(chan->music_state); chan->music_state = NULL; } } static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass) { struct mohclass *mohclass = NULL; /* The following is the order of preference for which class to use: * 1) The channels explicitly set musicclass, which should *only* be * set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan. * 2) The mclass argument. If a channel is calling ast_moh_start() as the * result of receiving a HOLD control frame, this should be the * payload that came with the frame. * 3) The interpclass argument. This would be from the mohinterpret * option from channel drivers. This is the same as the old musicclass * option. * 4) The default class. */ AST_LIST_LOCK(&mohclasses); if (!ast_strlen_zero(chan->musicclass)) mohclass = get_mohbyname(chan->musicclass, 1); if (!mohclass && !ast_strlen_zero(mclass)) mohclass = get_mohbyname(mclass, 1); if (!mohclass && !ast_strlen_zero(interpclass)) mohclass = get_mohbyname(interpclass, 1); if (!mohclass) mohclass = get_mohbyname("default", 1); if (mohclass) ast_atomic_fetchadd_int(&mohclass->inuse, +1); AST_LIST_UNLOCK(&mohclasses); if (!mohclass) return -1; ast_set_flag(chan, AST_FLAG_MOH); if (mohclass->total_files) { return ast_activate_generator(chan, &moh_file_stream, mohclass); } else return ast_activate_generator(chan, &mohgen, mohclass); } static void local_ast_moh_stop(struct ast_channel *chan) { ast_clear_flag(chan, AST_FLAG_MOH); ast_deactivate_generator(chan); if (chan->music_state) { if (chan->stream) { ast_closestream(chan->stream); chan->stream = NULL; } } } static struct mohclass *moh_class_malloc(void) { struct mohclass *class; if ((class = ast_calloc(1, sizeof(*class)))) class->format = AST_FORMAT_SLINEAR; return class; } static int load_moh_classes(int reload) { struct ast_config *cfg; struct ast_variable *var; struct mohclass *class; char *data; char *args; char *cat; int numclasses = 0; static int dep_warning = 0; cfg = ast_config_load("musiconhold.conf"); if (!cfg) return 0; if (reload) { AST_LIST_LOCK(&mohclasses); AST_LIST_TRAVERSE(&mohclasses, class, list) class->delete = 1; AST_LIST_UNLOCK(&mohclasses); } cat = ast_category_browse(cfg, NULL); for (; cat; cat = ast_category_browse(cfg, cat)) { if (strcasecmp(cat, "classes") && strcasecmp(cat, "moh_files")) { if (!(class = moh_class_malloc())) { break; } ast_copy_string(class->name, cat, sizeof(class->name)); var = ast_variable_browse(cfg, cat); while (var) { if (!strcasecmp(var->name, "mode")) ast_copy_string(class->mode, var->value, sizeof(class->mode)); else if (!strcasecmp(var->name, "directory")) ast_copy_string(class->dir, var->value, sizeof(class->dir)); else if (!strcasecmp(var->name, "application")) ast_copy_string(class->args, var->value, sizeof(class->args)); else if (!strcasecmp(var->name, "random")) ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE); else if (!strcasecmp(var->name, "format")) { class->format = ast_getformatbyname(var->value); if (!class->format) { ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value); class->format = AST_FORMAT_SLINEAR; } } var = var->next; } if (ast_strlen_zero(class->dir)) { if (!strcasecmp(class->mode, "custom")) { strcpy(class->dir, "nodir"); } else { ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name); free(class); continue; } } if (ast_strlen_zero(class->mode)) { ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name); free(class); continue; } if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) { ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name); free(class); continue; } /* Don't leak a class when it's already registered */ moh_register(class, reload); numclasses++; } } /* Deprecated Old-School Configuration */ var = ast_variable_browse(cfg, "classes"); while (var) { if (!dep_warning) { ast_log(LOG_WARNING, "The old musiconhold.conf syntax has been deprecated! Please refer to the sample configuration for information on the new syntax.\n"); dep_warning = 1; } data = strchr(var->value, ':'); if (data) { *data++ = '\0'; args = strchr(data, ','); if (args) *args++ = '\0'; if (!(get_mohbyname(var->name, 0))) { if (!(class = moh_class_malloc())) { break; } ast_copy_string(class->name, var->name, sizeof(class->name)); ast_copy_string(class->dir, data, sizeof(class->dir)); ast_copy_string(class->mode, var->value, sizeof(class->mode)); if (args) ast_copy_string(class->args, args, sizeof(class->args)); moh_register(class, reload); numclasses++; } } var = var->next; } var = ast_variable_browse(cfg, "moh_files"); while (var) { if (!dep_warning) { ast_log(LOG_WARNING, "The old musiconhold.conf syntax has been deprecated! Please refer to the sample configuration for information on the new syntax.\n"); dep_warning = 1; } if (!(get_mohbyname(var->name, 0))) { args = strchr(var->value, ','); if (args) *args++ = '\0'; if (!(class = moh_class_malloc())) { break; } ast_copy_string(class->name, var->name, sizeof(class->name)); ast_copy_string(class->dir, var->value, sizeof(class->dir)); strcpy(class->mode, "files"); if (args) ast_copy_string(class->args, args, sizeof(class->args)); moh_register(class, reload); numclasses++; } var = var->next; } ast_config_destroy(cfg); return numclasses; } static int ast_moh_destroy_one(struct mohclass *moh) { char buff[8192]; int bytes, tbytes = 0, stime = 0, pid = 0; if (moh) { if (moh->pid > 1) { ast_log(LOG_DEBUG, "killing %d!\n", moh->pid); stime = time(NULL) + 2; pid = moh->pid; moh->pid = 0; /* Back when this was just mpg123, SIGKILL was fine. Now we need * to give the process a reason and time enough to kill off its * children. */ kill(pid, SIGHUP); usleep(100000); kill(pid, SIGTERM); usleep(100000); kill(pid, SIGKILL); while ((ast_wait_for_input(moh->srcfd, 100) > 0) && (bytes = read(moh->srcfd, buff, 8192)) && time(NULL) < stime) tbytes = tbytes + bytes; ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes); close(moh->srcfd); } ast_moh_free_class(&moh); } return 0; } static void ast_moh_destroy(void) { struct mohclass *moh; if (option_verbose > 1) ast_verbose(VERBOSE_PREFIX_2 "Destroying musiconhold processes\n"); AST_LIST_LOCK(&mohclasses); while ((moh = AST_LIST_REMOVE_HEAD(&mohclasses, list))) { ast_moh_destroy_one(moh); } AST_LIST_UNLOCK(&mohclasses); } static int moh_cli(int fd, int argc, char *argv[]) { reload(); return 0; } static int cli_files_show(int fd, int argc, char *argv[]) { int i; struct mohclass *class; AST_LIST_LOCK(&mohclasses); AST_LIST_TRAVERSE(&mohclasses, class, list) { if (!class->total_files) continue; ast_cli(fd, "Class: %s\n", class->name); for (i = 0; i < class->total_files; i++) ast_cli(fd, "\tFile: %s\n", class->filearray[i]); } AST_LIST_UNLOCK(&mohclasses); return 0; } static int moh_classes_show(int fd, int argc, char *argv[]) { struct mohclass *class; AST_LIST_LOCK(&mohclasses); AST_LIST_TRAVERSE(&mohclasses, class, list) { ast_cli(fd, "Class: %s\n", class->name); ast_cli(fd, "\tMode: %s\n", S_OR(class->mode, "")); ast_cli(fd, "\tDirectory: %s\n", S_OR(class->dir, "")); ast_cli(fd, "\tUse Count: %d\n", class->inuse); if (ast_test_flag(class, MOH_CUSTOM)) ast_cli(fd, "\tApplication: %s\n", S_OR(class->args, "")); if (strcasecmp(class->mode, "files")) ast_cli(fd, "\tFormat: %s\n", ast_getformatname(class->format)); } AST_LIST_UNLOCK(&mohclasses); return 0; } static struct ast_cli_entry cli_moh_classes_show_deprecated = { { "moh", "classes", "show"}, moh_classes_show, NULL, NULL }; static struct ast_cli_entry cli_moh_files_show_deprecated = { { "moh", "files", "show"}, cli_files_show, NULL, NULL }; static struct ast_cli_entry cli_moh[] = { { { "moh", "reload"}, moh_cli, "Music On Hold", "Usage: moh reload\n Rereads configuration\n" }, { { "moh", "show", "classes"}, moh_classes_show, "List MOH classes", "Usage: moh show classes\n Lists all MOH classes\n", NULL, &cli_moh_classes_show_deprecated }, { { "moh", "show", "files"}, cli_files_show, "List MOH file-based classes", "Usage: moh show files\n Lists all loaded file-based MOH classes and their files\n", NULL, &cli_moh_files_show_deprecated }, }; static int init_classes(int reload) { struct mohclass *moh; if (!load_moh_classes(reload)) /* Load classes from config */ return 0; /* Return if nothing is found */ AST_LIST_LOCK(&mohclasses); AST_LIST_TRAVERSE_SAFE_BEGIN(&mohclasses, moh, list) { if (reload && moh->delete) { AST_LIST_REMOVE_CURRENT(&mohclasses, list); if (!moh->inuse) ast_moh_destroy_one(moh); } } AST_LIST_TRAVERSE_SAFE_END AST_LIST_UNLOCK(&mohclasses); return 1; } static int load_module(void) { int res; res = ast_register_application(app0, moh0_exec, synopsis0, descrip0); ast_register_atexit(ast_moh_destroy); ast_cli_register_multiple(cli_moh, sizeof(cli_moh) / sizeof(struct ast_cli_entry)); if (!res) res = ast_register_application(app1, moh1_exec, synopsis1, descrip1); if (!res) res = ast_register_application(app2, moh2_exec, synopsis2, descrip2); if (!res) res = ast_register_application(app3, moh3_exec, synopsis3, descrip3); if (!res) res = ast_register_application(app4, moh4_exec, synopsis4, descrip4); if (!init_classes(0)) { /* No music classes configured, so skip it */ ast_log(LOG_WARNING, "No music on hold classes configured, disabling music on hold.\n"); } else { ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup); } return 0; } static int reload(void) { if (init_classes(1)) ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup); return 0; } static int unload_module(void) { int res = 0; struct mohclass *class = NULL; AST_LIST_LOCK(&mohclasses); AST_LIST_TRAVERSE(&mohclasses, class, list) { if (class->inuse > 0) { res = -1; break; } } AST_LIST_UNLOCK(&mohclasses); if (res < 0) { ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n"); return res; } ast_uninstall_music_functions(); ast_moh_destroy(); res = ast_unregister_application(app0); res |= ast_unregister_application(app1); res |= ast_unregister_application(app2); res |= ast_unregister_application(app3); res |= ast_unregister_application(app4); ast_cli_unregister_multiple(cli_moh, sizeof(cli_moh) / sizeof(struct ast_cli_entry)); return res; } AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Music On Hold Resource", .load = load_module, .unload = unload_module, .reload = reload, ); asterisk-1.4.21.2/res/res_snmp.c0000644000175000017500000000522710631630102015607 0ustar maniacmaniac/* * Copyright (C) 2006 Voop as * Thorsten Lockert * * 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 SNMP Agent / SubAgent support for Asterisk * * \author Thorsten Lockert */ /*** MODULEINFO netsnmp ***/ #include "asterisk.h" ASTERISK_FILE_VERSION(__FILE__, "$Revision: 67872 $") #include "asterisk/channel.h" #include "asterisk/module.h" #include "asterisk/logger.h" #include "asterisk/options.h" #include "snmp/agent.h" #define MODULE_DESCRIPTION "SNMP [Sub]Agent for Asterisk" int res_snmp_agentx_subagent; int res_snmp_dont_stop; int res_snmp_enabled; static pthread_t thread = AST_PTHREADT_NULL; static int load_config(void) { struct ast_variable *var; struct ast_config *cfg; char *cat; res_snmp_enabled = 0; res_snmp_agentx_subagent = 1; cfg = ast_config_load("res_snmp.conf"); if (!cfg) { ast_log(LOG_WARNING, "Could not load res_snmp.conf\n"); return 0; } cat = ast_category_browse(cfg, NULL); while (cat) { var = ast_variable_browse(cfg, cat); if (strcasecmp(cat, "general") == 0) { while (var) { if (strcasecmp(var->name, "subagent") == 0) { if (ast_true(var->value)) res_snmp_agentx_subagent = 1; else if (ast_false(var->value)) res_snmp_agentx_subagent = 0; else { ast_log(LOG_ERROR, "Value '%s' does not evaluate to true or false.\n", var->value); ast_config_destroy(cfg); return 1; } } else if (strcasecmp(var->name, "enabled") == 0) { res_snmp_enabled = ast_true(var->value); } else { ast_log(LOG_ERROR, "Unrecognized variable '%s' in category '%s'\n", var->name, cat); ast_config_destroy(cfg); return 1; } var = var->next; } } else { ast_log(LOG_ERROR, "Unrecognized category '%s'\n", cat); ast_config_destroy(cfg); return 1; } cat = ast_category_browse(cfg, cat); } ast_config_destroy(cfg); return 1; } static int load_module(void) { if(!load_config()) return AST_MODULE_LOAD_DECLINE; ast_verbose(VERBOSE_PREFIX_1 "Loading [Sub]Agent Module\n"); res_snmp_dont_stop = 1; if (res_snmp_enabled) return ast_pthread_create_background(&thread, NULL, agent_thread, NULL); else return 0; } static int unload_module(void) { ast_verbose(VERBOSE_PREFIX_1 "Unloading [Sub]Agent Module\n"); res_snmp_dont_stop = 0; return ((thread != AST_PTHREADT_NULL) ? pthread_join(thread, NULL) : 0); } AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "SNMP [Sub]Agent for Asterisk", .load = load_module, .unload = unload_module, ); asterisk-1.4.21.2/res/res_jabber.c0000644000175000017500000022530211022015257016060 0ustar maniacmaniac/* * Asterisk -- An open source telephony toolkit. * * Copyright (C) 1999 - 2006, Digium, Inc. * * Matt O'Gorman * * 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 resource for interfacing asterisk directly as a client * or a component to a jabber compliant server. * * \todo If you unload this module, chan_gtalk/jingle will be dead. How do we handle that? * \todo If you have TLS, you can't unload this module. See bug #9738. This needs to be fixed, * but the bug is in the unmantained Iksemel library * */ /*** MODULEINFO iksemel gnutls ***/ #include "asterisk.h" ASTERISK_FILE_VERSION(__FILE__, "$Revision: 120675 $") #include #include #include #include "asterisk/channel.h" #include "asterisk/jabber.h" #include "asterisk/file.h" #include "asterisk/config.h" #include "asterisk/callerid.h" #include "asterisk/lock.h" #include "asterisk/logger.h" #include "asterisk/options.h" #include "asterisk/cli.h" #include "asterisk/app.h" #include "asterisk/pbx.h" #include "asterisk/md5.h" #include "asterisk/acl.h" #include "asterisk/utils.h" #include "asterisk/module.h" #include "asterisk/astobj.h" #include "asterisk/astdb.h" #include "asterisk/manager.h" #define JABBER_CONFIG "jabber.conf" #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE 1 #endif /*-- Forward declarations */ static int aji_highest_bit(int number); static void aji_buddy_destroy(struct aji_buddy *obj); static void aji_client_destroy(struct aji_client *obj); static int aji_send_exec(struct ast_channel *chan, void *data); static int aji_status_exec(struct ast_channel *chan, void *data); static void aji_log_hook(void *data, const char *xmpp, size_t size, int is_incoming); static int aji_act_hook(void *data, int type, iks *node); static void aji_handle_iq(struct aji_client *client, iks *node); static void aji_handle_message(struct aji_client *client, ikspak *pak); static void aji_handle_presence(struct aji_client *client, ikspak *pak); static void aji_handle_subscribe(struct aji_client *client, ikspak *pak); static void *aji_recv_loop(void *data); static int aji_component_initialize(struct aji_client *client); static int aji_client_initialize(struct aji_client *client); static int aji_client_connect(void *data, ikspak *pak); static void aji_set_presence(struct aji_client *client, char *to, char *from, int level, char *desc); static int aji_do_debug(int fd, int argc, char *argv[]); static int aji_do_reload(int fd, int argc, char *argv[]); static int aji_no_debug(int fd, int argc, char *argv[]); static int aji_test(int fd, int argc, char *argv[]); static int aji_show_clients(int fd, int argc, char *argv[]); static int aji_create_client(char *label, struct ast_variable *var, int debug); static int aji_create_buddy(char *label, struct aji_client *client); static int aji_reload(void); static int aji_load_config(void); static void aji_pruneregister(struct aji_client *client); static int aji_filter_roster(void *data, ikspak *pak); static int aji_get_roster(struct aji_client *client); static int aji_client_info_handler(void *data, ikspak *pak); static int aji_dinfo_handler(void *data, ikspak *pak); static int aji_ditems_handler(void *data, ikspak *pak); static int aji_register_query_handler(void *data, ikspak *pak); static int aji_register_approve_handler(void *data, ikspak *pak); static int aji_reconnect(struct aji_client *client); static iks *jabber_make_auth(iksid * id, const char *pass, const char *sid); /* No transports in this version */ /* static int aji_create_transport(char *label, struct aji_client *client); static int aji_register_transport(void *data, ikspak *pak); static int aji_register_transport2(void *data, ikspak *pak); */ static char debug_usage[] = "Usage: jabber debug\n" " Enables dumping of Jabber packets for debugging purposes.\n"; static char no_debug_usage[] = "Usage: jabber debug off\n" " Disables dumping of Jabber packets for debugging purposes.\n"; static char reload_usage[] = "Usage: jabber reload\n" " Enables reloading of Jabber module.\n"; static char test_usage[] = "Usage: jabber test [client]\n" " Sends test message for debugging purposes. A specific client\n" " as configured in jabber.conf can be optionally specified.\n"; static struct ast_cli_entry aji_cli[] = { { { "jabber", "debug", NULL}, aji_do_debug, "Enable Jabber debugging", debug_usage }, { { "jabber", "reload", NULL}, aji_do_reload, "Reload Jabber configuration", reload_usage }, { { "jabber", "show", "connected", NULL}, aji_show_clients, "Show state of clients and components", debug_usage }, { { "jabber", "debug", "off", NULL}, aji_no_debug, "Disable Jabber debug", no_debug_usage }, { { "jabber", "test", NULL}, aji_test, "Shows roster, but is generally used for mog's debugging.", test_usage }, }; static char *app_ajisend = "JabberSend"; static char *ajisend_synopsis = "JabberSend(jabber,screenname,message)"; static char *ajisend_descrip = "JabberSend(Jabber,ScreenName,Message)\n" " Jabber - Client or transport Asterisk uses to connect to Jabber\n" " ScreenName - User Name to message.\n" " Message - Message to be sent to the buddy\n"; static char *app_ajistatus = "JabberStatus"; static char *ajistatus_synopsis = "JabberStatus(Jabber,ScreenName,Variable)"; static char *ajistatus_descrip = "JabberStatus(Jabber,ScreenName,Variable)\n" " Jabber - Client or transport Asterisk uses to connect to Jabber\n" " ScreenName - User Name to retrieve status from.\n" " Variable - Variable to store presence in will be 1-6.\n" " In order, Online, Chatty, Away, XAway, DND, Offline\n" " If not in roster variable will = 7\n"; struct aji_client_container clients; struct aji_capabilities *capabilities = NULL; /*! \brief Global flags, initialized to default values */ static struct ast_flags globalflags = { AJI_AUTOPRUNE | AJI_AUTOREGISTER }; static int tls_initialized = FALSE; /*! * \brief Deletes the aji_client data structure. * \param obj is the structure we will delete. * \return void. */ static void aji_client_destroy(struct aji_client *obj) { struct aji_message *tmp; ASTOBJ_CONTAINER_DESTROYALL(&obj->buddies, aji_buddy_destroy); ASTOBJ_CONTAINER_DESTROY(&obj->buddies); iks_filter_delete(obj->f); iks_parser_delete(obj->p); iks_stack_delete(obj->stack); AST_LIST_LOCK(&obj->messages); while ((tmp = AST_LIST_REMOVE_HEAD(&obj->messages, list))) { if (tmp->from) free(tmp->from); if (tmp->message) free(tmp->message); } AST_LIST_HEAD_DESTROY(&obj->messages); free(obj); } /*! * \brief Deletes the aji_buddy data structure. * \param obj is the structure we will delete. * \return void. */ static void aji_buddy_destroy(struct aji_buddy *obj) { struct aji_resource *tmp; while ((tmp = obj->resources)) { obj->resources = obj->resources->next; free(tmp->description); free(tmp); } free(obj); } /*! * \brief Find version in XML stream and populate our capabilities list * \param node the node attribute in the caps element we'll look for or add to * our list * \param version the version attribute in the caps element we'll look for or * add to our list * \param pak the XML stanza we're processing * \return a pointer to the added or found aji_version structure */ static struct aji_version *aji_find_version(char *node, char *version, ikspak *pak) { struct aji_capabilities *list = NULL; struct aji_version *res = NULL; list = capabilities; if(!node) node = pak->from->full; if(!version) version = "none supplied."; while(list) { if(!strcasecmp(list->node, node)) { res = list->versions; while(res) { if(!strcasecmp(res->version, version)) return res; res = res->next; } /* Specified version not found. Let's add it to this node in our capabilities list */ if(!res) { res = (struct aji_version *)malloc(sizeof(struct aji_version)); if(!res) { ast_log(LOG_ERROR, "Out of memory!\n"); return NULL; } res->jingle = 0; res->parent = list; ast_copy_string(res->version, version, sizeof(res->version)); res->next = list->versions; list->versions = res; return res; } } list = list->next; } /* Specified node not found. Let's add it our capabilities list */ if(!list) { list = (struct aji_capabilities *)malloc(sizeof(struct aji_capabilities)); if(!list) { ast_log(LOG_ERROR, "Out of memory!\n"); return NULL; } res = (struct aji_version *)malloc(sizeof(struct aji_version)); if(!res) { ast_log(LOG_ERROR, "Out of memory!\n"); ast_free(list); return NULL; } ast_copy_string(list->node, node, sizeof(list->node)); ast_copy_string(res->version, version, sizeof(res->version)); res->jingle = 0; res->parent = list; res->next = NULL; list->versions = res; list->next = capabilities; capabilities = list; } return res; } static struct aji_resource *aji_find_resource(struct aji_buddy *buddy, char *name) { struct aji_resource *res = NULL; if (!buddy || !name) return res; res = buddy->resources; while (res) { if (!strcasecmp(res->resource, name)) { break; } res = res->next; } return res; } static int gtalk_yuck(iks *node) { if (iks_find_with_attrib(node, "c", "node", "http://www.google.com/xmpp/client/caps")) return 1; return 0; } /*! * \brief Detects the highest bit in a number. * \param Number you want to have evaluated. * \return the highest power of 2 that can go into the number. */ static int aji_highest_bit(int number) { int x = sizeof(number) * 8 - 1; if (!number) return 0; for (; x > 0; x--) { if (number & (1 << x)) break; } return (1 << x); } static iks *jabber_make_auth(iksid * id, const char *pass, const char *sid) { iks *x, *y; x = iks_new("iq"); iks_insert_attrib(x, "type", "set"); y = iks_insert(x, "query"); iks_insert_attrib(y, "xmlns", IKS_NS_AUTH); iks_insert_cdata(iks_insert(y, "username"), id->user, 0); iks_insert_cdata(iks_insert(y, "resource"), id->resource, 0); if (sid) { char buf[41]; char sidpass[100]; snprintf(sidpass, sizeof(sidpass), "%s%s", sid, pass); ast_sha1_hash(buf, sidpass); iks_insert_cdata(iks_insert(y, "digest"), buf, 0); } else { iks_insert_cdata(iks_insert(y, "password"), pass, 0); } return x; } /*! * \brief Dial plan function status(). puts the status of watched user into a channel variable. * \param channel, and username,watched user, status var * \return 0. */ static int aji_status_exec(struct ast_channel *chan, void *data) { struct aji_client *client = NULL; struct aji_buddy *buddy = NULL; struct aji_resource *r = NULL; char *s = NULL, *sender = NULL, *jid = NULL, *screenname = NULL, *resource = NULL, *variable = NULL; int stat = 7; char status[2]; if (!data) { ast_log(LOG_ERROR, "This application requires arguments.\n"); return 0; } s = ast_strdupa(data); if (s) { sender = strsep(&s, "|"); if (sender && (sender[0] != '\0')) { jid = strsep(&s, "|"); if (jid && (jid[0] != '\0')) { variable = s; } else { ast_log(LOG_ERROR, "Bad arguments\n"); return -1; } } } if(!strchr(jid, '/')) { resource = NULL; } else { screenname = strsep(&jid, "/"); resource = jid; } client = ast_aji_get_client(sender); if (!client) { ast_log(LOG_WARNING, "Could not find sender connection: %s\n", sender); return -1; } if(!&client->buddies) { ast_log(LOG_WARNING, "No buddies for connection : %s\n", sender); return -1; } buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, resource ? screenname: jid); if (!buddy) { ast_log(LOG_WARNING, "Could not find buddy in list : %s\n", resource ? screenname : jid); return -1; } r = aji_find_resource(buddy, resource); if(!r && buddy->resources) r = buddy->resources; if(!r) ast_log(LOG_NOTICE, "Resource %s of buddy %s not found \n", resource, screenname); else stat = r->status; sprintf(status, "%d", stat); pbx_builtin_setvar_helper(chan, variable, status); return 0; } /*! * \brief Dial plan function to send a message. * \param channel, and data, data is sender, reciever, message. * \return 0. */ static int aji_send_exec(struct ast_channel *chan, void *data) { struct aji_client *client = NULL; char *s = NULL, *sender = NULL, *recipient = NULL, *message = NULL; if (!data) { ast_log(LOG_ERROR, "This application requires arguments.\n"); return 0; } s = ast_strdupa(data); if (s) { sender = strsep(&s, "|"); if (sender && (sender[0] != '\0')) { recipient = strsep(&s, "|"); if (recipient && (recipient[0] != '\0')) { message = s; } else { ast_log(LOG_ERROR, "Bad arguments: %s\n", (char *) data); return -1; } } } if (!(client = ast_aji_get_client(sender))) { ast_log(LOG_WARNING, "Could not find sender connection: %s\n", sender); return -1; } if (strchr(recipient, '@') && message) ast_aji_send(client, recipient, message); return 0; } /*! * \brief the debug loop. * \param aji_client structure, xml data as string, size of string, direction of packet, 1 for inbound 0 for outbound. */ static void aji_log_hook(void *data, const char *xmpp, size_t size, int is_incoming) { struct aji_client *client = ASTOBJ_REF((struct aji_client *) data); manager_event(EVENT_FLAG_USER, "JabberEvent", "Account: %s\r\nPacket: %s\r\n", client->name, xmpp); if (client->debug) { if (is_incoming) ast_verbose("\nJABBER: %s INCOMING: %s\n", client->name, xmpp); else { if( strlen(xmpp) == 1) { if(option_debug > 2 && xmpp[0] == ' ') ast_verbose("\nJABBER: Keep alive packet\n"); } else ast_verbose("\nJABBER: %s OUTGOING: %s\n", client->name, xmpp); } } ASTOBJ_UNREF(client, aji_client_destroy); } /*! * \brief The action hook parses the inbound packets, constantly running. * \param data aji client structure * \param type type of packet * \param node the actual packet. * \return IKS_OK or IKS_HOOK . */ static int aji_act_hook(void *data, int type, iks *node) { struct aji_client *client = ASTOBJ_REF((struct aji_client *) data); ikspak *pak = NULL; iks *auth = NULL; if(!node) { ast_log(LOG_ERROR, "aji_act_hook was called with out a packet\n"); /* most likely cause type is IKS_NODE_ERROR lost connection */ ASTOBJ_UNREF(client, aji_client_destroy); return IKS_HOOK; } if (client->state == AJI_DISCONNECTING) { ASTOBJ_UNREF(client, aji_client_destroy); return IKS_HOOK; } pak = iks_packet(node); if (!client->component) { /*client */ switch (type) { case IKS_NODE_START: if (client->usetls && !iks_is_secure(client->p)) { if (iks_has_tls()) { iks_start_tls(client->p); tls_initialized = TRUE; } else ast_log(LOG_ERROR, "gnuTLS not installed. You need to recompile the Iksemel library with gnuTLS support\n"); break; } if (!client->usesasl) { iks_filter_add_rule(client->f, aji_client_connect, client, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_ID, client->mid, IKS_RULE_DONE); auth = jabber_make_auth(client->jid, client->password, iks_find_attrib(node, "id")); if (auth) { iks_insert_attrib(auth, "id", client->mid); iks_insert_attrib(auth, "to", client->jid->server); ast_aji_increment_mid(client->mid); iks_send(client->p, auth); iks_delete(auth); } else ast_log(LOG_ERROR, "Out of memory.\n"); } break; case IKS_NODE_NORMAL: if (!strcmp("stream:features", iks_name(node))) { int features = 0; features = iks_stream_features(node); if (client->usesasl) { if (client->usetls && !iks_is_secure(client->p)) break; if (client->authorized) { if (features & IKS_STREAM_BIND) { iks_filter_add_rule (client->f, aji_client_connect, client, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_DONE); auth = iks_make_resource_bind(client->jid); if (auth) { iks_insert_attrib(auth, "id", client->mid); ast_aji_increment_mid(client->mid); iks_send(client->p, auth); iks_delete(auth); } else { ast_log(LOG_ERROR, "Out of memory.\n"); break; } } if (features & IKS_STREAM_SESSION) { iks_filter_add_rule (client->f, aji_client_connect, client, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_ID, "auth", IKS_RULE_DONE); auth = iks_make_session(); if (auth) { iks_insert_attrib(auth, "id", "auth"); ast_aji_increment_mid(client->mid); iks_send(client->p, auth); iks_delete(auth); } else { ast_log(LOG_ERROR, "Out of memory.\n"); } } } else { if (!client->jid->user) { ast_log(LOG_ERROR, "Malformed Jabber ID : %s (domain missing?)\n", client->jid->full); break; } features = aji_highest_bit(features); if (features == IKS_STREAM_SASL_MD5) iks_start_sasl(client->p, IKS_SASL_DIGEST_MD5, client->jid->user, client->password); else { if (features == IKS_STREAM_SASL_PLAIN) { iks *x = NULL; x = iks_new("auth"); if (x) { int len = strlen(client->jid->user) + strlen(client->password) + 3; /* XXX Check return values XXX */ char *s = ast_malloc(80 + len); char *base64 = ast_malloc(80 + len * 2); iks_insert_attrib(x, "xmlns", IKS_NS_XMPP_SASL); iks_insert_attrib(x, "mechanism", "PLAIN"); sprintf(s, "%c%s%c%s", 0, client->jid->user, 0, client->password); /* exclude the NULL training byte from the base64 encoding operation as some XMPP servers will refuse it. The format for authentication is [authzid]\0authcid\0password not [authzid]\0authcid\0password\0 */ ast_base64encode(base64, (const unsigned char *) s, len - 1, len * 2); iks_insert_cdata(x, base64, 0); iks_send(client->p, x); iks_delete(x); if (base64) free(base64); if (s) free(s); } else { ast_log(LOG_ERROR, "Out of memory.\n"); } } } } } } else if (!strcmp("failure", iks_name(node))) { ast_log(LOG_ERROR, "JABBER: encryption failure. possible bad password.\n"); } else if (!strcmp("success", iks_name(node))) { client->authorized = 1; iks_send_header(client->p, client->jid->server); } break; case IKS_NODE_ERROR: ast_log(LOG_ERROR, "JABBER: Node Error\n"); ASTOBJ_UNREF(client, aji_client_destroy); return IKS_HOOK; break; case IKS_NODE_STOP: ast_log(LOG_WARNING, "JABBER: Disconnected\n"); ASTOBJ_UNREF(client, aji_client_destroy); return IKS_HOOK; break; } } else if (client->state != AJI_CONNECTED && client->component) { switch (type) { case IKS_NODE_START: if (client->state == AJI_DISCONNECTED) { char secret[160], shasum[320], *handshake; sprintf(secret, "%s%s", pak->id, client->password); ast_sha1_hash(shasum, secret); handshake = NULL; asprintf(&handshake, "%s", shasum); if (handshake) { iks_send_raw(client->p, handshake); free(handshake); handshake = NULL; } client->state = AJI_CONNECTING; if(iks_recv(client->p,1) == 2) /*XXX proper result for iksemel library on iks_recv of XXX*/ client->state = AJI_CONNECTED; else ast_log(LOG_WARNING,"Jabber didn't seem to handshake, failed to authenicate.\n"); break; } break; case IKS_NODE_NORMAL: break; case IKS_NODE_ERROR: ast_log(LOG_ERROR, "JABBER: Node Error\n"); ASTOBJ_UNREF(client, aji_client_destroy); return IKS_HOOK; case IKS_NODE_STOP: ast_log(LOG_WARNING, "JABBER: Disconnected\n"); ASTOBJ_UNREF(client, aji_client_destroy); return IKS_HOOK; } } switch (pak->type) { case IKS_PAK_NONE: if (option_debug) ast_log(LOG_DEBUG, "JABBER: I Don't know what to do with you NONE\n"); break; case IKS_PAK_MESSAGE: aji_handle_message(client, pak); if (option_debug) ast_log(LOG_DEBUG, "JABBER: I Don't know what to do with you MESSAGE\n"); break; case IKS_PAK_PRESENCE: aji_handle_presence(client, pak); if (option_debug) ast_log(LOG_DEBUG, "JABBER: I Do know how to handle presence!!\n"); break; case IKS_PAK_S10N: aji_handle_subscribe(client, pak); if (option_debug) ast_log(LOG_DEBUG, "JABBER: I Dont know S10N subscribe!!\n"); break; case IKS_PAK_IQ: if (option_debug) ast_log(LOG_DEBUG, "JABBER: I Dont have an IQ!!!\n"); aji_handle_iq(client, node); break; default: if (option_debug) ast_log(LOG_DEBUG, "JABBER: I Dont know %i\n", pak->type); break; } iks_filter_packet(client->f, pak); if (node) iks_delete(node); ASTOBJ_UNREF(client, aji_client_destroy); return IKS_OK; } static int aji_register_approve_handler(void *data, ikspak *pak) { struct aji_client *client = ASTOBJ_REF((struct aji_client *) data); iks *iq = NULL, *presence = NULL, *x = NULL; iq = iks_new("iq"); presence = iks_new("presence"); x = iks_new("x"); if (client && iq && presence && x) { if (!iks_find(pak->query, "remove")) { iks_insert_attrib(iq, "from", client->jid->full); iks_insert_attrib(iq, "to", pak->from->full); iks_insert_attrib(iq, "id", pak->id); iks_insert_attrib(iq, "type", "result"); iks_send(client->p, iq); iks_insert_attrib(presence, "from", client->jid->full); iks_insert_attrib(presence, "to", pak->from->partial); iks_insert_attrib(presence, "id", client->mid); ast_aji_increment_mid(client->mid); iks_insert_attrib(presence, "type", "subscribe"); iks_insert_attrib(x, "xmlns", "vcard-temp:x:update"); iks_insert_node(presence, x); iks_send(client->p, presence); } } else { ast_log(LOG_ERROR, "Out of memory.\n"); } if (iq) iks_delete(iq); if(presence) iks_delete(presence); if (x) iks_delete(x); ASTOBJ_UNREF(client, aji_client_destroy); return IKS_FILTER_EAT; } static int aji_register_query_handler(void *data, ikspak *pak) { struct aji_client *client = ASTOBJ_REF((struct aji_client *) data); struct aji_buddy *buddy = NULL; char *node = NULL; client = (struct aji_client *) data; buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial); if (!buddy) { iks *iq = NULL, *query = NULL, *error = NULL, *notacceptable = NULL; ast_verbose("Someone.... %s tried to register but they aren't allowed\n", pak->from->partial); iq = iks_new("iq"); query = iks_new("query"); error = iks_new("error"); notacceptable = iks_new("not-acceptable"); if(iq && query && error && notacceptable) { iks_insert_attrib(iq, "type", "error"); iks_insert_attrib(iq, "from", client->user); iks_insert_attrib(iq, "to", pak->from->full); iks_insert_attrib(iq, "id", pak->id); iks_insert_attrib(query, "xmlns", "jabber:iq:register"); iks_insert_attrib(error, "code" , "406"); iks_insert_attrib(error, "type", "modify"); iks_insert_attrib(notacceptable, "xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas"); iks_insert_node(iq, query); iks_insert_node(iq, error); iks_insert_node(error, notacceptable); iks_send(client->p, iq); } else { ast_log(LOG_ERROR, "Out of memory.\n"); } if (iq) iks_delete(iq); if (query) iks_delete(query); if (error) iks_delete(error); if (notacceptable) iks_delete(notacceptable); } else if (!(node = iks_find_attrib(pak->query, "node"))) { iks *iq = NULL, *query = NULL, *instructions = NULL; char *explain = "Welcome to Asterisk - the Open Source PBX.\n"; iq = iks_new("iq"); query = iks_new("query"); instructions = iks_new("instructions"); if (iq && query && instructions && client) { iks_insert_attrib(iq, "from", client->user); iks_insert_attrib(iq, "to", pak->from->full); iks_insert_attrib(iq, "id", pak->id); iks_insert_attrib(iq, "type", "result"); iks_insert_attrib(query, "xmlns", "jabber:iq:register"); iks_insert_cdata(instructions, explain, 0); iks_insert_node(iq, query); iks_insert_node(query, instructions); iks_send(client->p, iq); } else { ast_log(LOG_ERROR, "Out of memory.\n"); } if (iq) iks_delete(iq); if (query) iks_delete(query); if (instructions) iks_delete(instructions); } ASTOBJ_UNREF(client, aji_client_destroy); return IKS_FILTER_EAT; } static int aji_ditems_handler(void *data, ikspak *pak) { struct aji_client *client = ASTOBJ_REF((struct aji_client *) data); char *node = NULL; if (!(node = iks_find_attrib(pak->query, "node"))) { iks *iq = NULL, *query = NULL, *item = NULL; iq = iks_new("iq"); query = iks_new("query"); item = iks_new("item"); if (iq && query && item) { iks_insert_attrib(iq, "from", client->user); iks_insert_attrib(iq, "to", pak->from->full); iks_insert_attrib(iq, "id", pak->id); iks_insert_attrib(iq, "type", "result"); iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items"); iks_insert_attrib(item, "node", "http://jabber.org/protocol/commands"); iks_insert_attrib(item, "name", "Million Dollar Asterisk Commands"); iks_insert_attrib(item, "jid", client->user); iks_insert_node(iq, query); iks_insert_node(query, item); iks_send(client->p, iq); } else { ast_log(LOG_ERROR, "Out of memory.\n"); } if (iq) iks_delete(iq); if (query) iks_delete(query); if (item) iks_delete(item); } else if (!strcasecmp(node, "http://jabber.org/protocol/commands")) { iks *iq, *query, *confirm; iq = iks_new("iq"); query = iks_new("query"); confirm = iks_new("item"); if (iq && query && confirm && client) { iks_insert_attrib(iq, "from", client->user); iks_insert_attrib(iq, "to", pak->from->full); iks_insert_attrib(iq, "id", pak->id); iks_insert_attrib(iq, "type", "result"); iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items"); iks_insert_attrib(query, "node", "http://jabber.org/protocol/commands"); iks_insert_attrib(confirm, "node", "confirmaccount"); iks_insert_attrib(confirm, "name", "Confirm AIM account"); iks_insert_attrib(confirm, "jid", "blog.astjab.org"); iks_insert_node(iq, query); iks_insert_node(query, confirm); iks_send(client->p, iq); } else { ast_log(LOG_ERROR, "Out of memory.\n"); } if (iq) iks_delete(iq); if (query) iks_delete(query); if (confirm) iks_delete(confirm); } else if (!strcasecmp(node, "confirmaccount")) { iks *iq = NULL, *query = NULL, *feature = NULL; iq = iks_new("iq"); query = iks_new("query"); feature = iks_new("feature"); if (iq && query && feature && client) { iks_insert_attrib(iq, "from", client->user); iks_insert_attrib(iq, "to", pak->from->full); iks_insert_attrib(iq, "id", pak->id); iks_insert_attrib(iq, "type", "result"); iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items"); iks_insert_attrib(feature, "var", "http://jabber.org/protocol/commands"); iks_insert_node(iq, query); iks_insert_node(query, feature); iks_send(client->p, iq); } else { ast_log(LOG_ERROR, "Out of memory.\n"); } if (iq) iks_delete(iq); if (query) iks_delete(query); if (feature) iks_delete(feature); } ASTOBJ_UNREF(client, aji_client_destroy); return IKS_FILTER_EAT; } static int aji_client_info_handler(void *data, ikspak *pak) { struct aji_client *client = ASTOBJ_REF((struct aji_client *) data); struct aji_resource *resource = NULL; struct aji_buddy *buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial); resource = aji_find_resource(buddy, pak->from->resource); if (pak->subtype == IKS_TYPE_RESULT) { if (!resource) { ast_log(LOG_NOTICE,"JABBER: Received client info from %s when not requested.\n", pak->from->full); ASTOBJ_UNREF(client, aji_client_destroy); return IKS_FILTER_EAT; } if (iks_find_with_attrib(pak->query, "feature", "var", "http://www.google.com/xmpp/protocol/voice/v1")) { resource->cap->jingle = 1; } else resource->cap->jingle = 0; } else if (pak->subtype == IKS_TYPE_GET) { iks *iq, *disco, *ident, *google, *query; iq = iks_new("iq"); query = iks_new("query"); ident = iks_new("identity"); disco = iks_new("feature"); google = iks_new("feature"); if (iq && ident && disco && google) { iks_insert_attrib(iq, "from", client->jid->full); iks_insert_attrib(iq, "to", pak->from->full); iks_insert_attrib(iq, "type", "result"); iks_insert_attrib(iq, "id", pak->id); iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info"); iks_insert_attrib(ident, "category", "client"); iks_insert_attrib(ident, "type", "pc"); iks_insert_attrib(ident, "name", "asterisk"); iks_insert_attrib(disco, "var", "http://jabber.org/protocol/disco#info"); iks_insert_attrib(google, "var", "http://www.google.com/xmpp/protocol/voice/v1"); iks_insert_node(iq, query); iks_insert_node(query, ident); iks_insert_node(query, google); iks_insert_node(query, disco); iks_send(client->p, iq); } else ast_log(LOG_ERROR, "Out of Memory.\n"); if (iq) iks_delete(iq); if (query) iks_delete(query); if (ident) iks_delete(ident); if (google) iks_delete(google); if (disco) iks_delete(disco); } else if (pak->subtype == IKS_TYPE_ERROR) { ast_log(LOG_NOTICE, "User %s does not support discovery.\n", pak->from->full); } ASTOBJ_UNREF(client, aji_client_destroy); return IKS_FILTER_EAT; } static int aji_dinfo_handler(void *data, ikspak *pak) { struct aji_client *client = ASTOBJ_REF((struct aji_client *) data); char *node = NULL; struct aji_resource *resource = NULL; struct aji_buddy *buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial); resource = aji_find_resource(buddy, pak->from->resource); if (pak->subtype == IKS_TYPE_ERROR) { ast_log(LOG_WARNING, "Recieved error from a client, turn on jabber debug!\n"); return IKS_FILTER_EAT; } if (pak->subtype == IKS_TYPE_RESULT) { if (!resource) { ast_log(LOG_NOTICE,"JABBER: Received client info from %s when not requested.\n", pak->from->full); ASTOBJ_UNREF(client, aji_client_destroy); return IKS_FILTER_EAT; } if (iks_find_with_attrib(pak->query, "feature", "var", "http://www.google.com/xmpp/protocol/voice/v1")) { resource->cap->jingle = 1; } else resource->cap->jingle = 0; } else if (pak->subtype == IKS_TYPE_GET && !(node = iks_find_attrib(pak->query, "node"))) { iks *iq, *query, *identity, *disco, *reg, *commands, *gateway, *version, *vcard, *search; iq = iks_new("iq"); query = iks_new("query"); identity = iks_new("identity"); disco = iks_new("feature"); reg = iks_new("feature"); commands = iks_new("feature"); gateway = iks_new("feature"); version = iks_new("feature"); vcard = iks_new("feature"); search = iks_new("feature"); if (iq && query && identity && disco && reg && commands && gateway && version && vcard && search && client) { iks_insert_attrib(iq, "from", client->user); iks_insert_attrib(iq, "to", pak->from->full); iks_insert_attrib(iq, "id", pak->id); iks_insert_attrib(iq, "type", "result"); iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info"); iks_insert_attrib(identity, "category", "gateway"); iks_insert_attrib(identity, "type", "pstn"); iks_insert_attrib(identity, "name", "Asterisk The Open Source PBX"); iks_insert_attrib(disco, "var", "http://jabber.org/protocol/disco"); iks_insert_attrib(reg, "var", "jabber:iq:register"); iks_insert_attrib(commands, "var", "http://jabber.org/protocol/commands"); iks_insert_attrib(gateway, "var", "jabber:iq:gateway"); iks_insert_attrib(version, "var", "jabber:iq:version"); iks_insert_attrib(vcard, "var", "vcard-temp"); iks_insert_attrib(search, "var", "jabber:iq:search"); iks_insert_node(iq, query); iks_insert_node(query, identity); iks_insert_node(query, disco); iks_insert_node(query, reg); iks_insert_node(query, commands); iks_insert_node(query, gateway); iks_insert_node(query, version); iks_insert_node(query, vcard); iks_insert_node(query, search); iks_send(client->p, iq); } else { ast_log(LOG_ERROR, "Out of memory.\n"); } if (iq) iks_delete(iq); if (query) iks_delete(query); if (identity) iks_delete(identity); if (disco) iks_delete(disco); if (reg) iks_delete(reg); if (commands) iks_delete(commands); if (gateway) iks_delete(gateway); if (version) iks_delete(version); if (vcard) iks_delete(vcard); if (search) iks_delete(search); } else if (pak->subtype == IKS_TYPE_GET && !strcasecmp(node, "http://jabber.org/protocol/commands")) { iks *iq, *query, *confirm; iq = iks_new("iq"); query = iks_new("query"); confirm = iks_new("item"); if (iq && query && confirm && client) { iks_insert_attrib(iq, "from", client->user); iks_insert_attrib(iq, "to", pak->from->full); iks_insert_attrib(iq, "id", pak->id); iks_insert_attrib(iq, "type", "result"); iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items"); iks_insert_attrib(query, "node", "http://jabber.org/protocol/commands"); iks_insert_attrib(confirm, "node", "confirmaccount"); iks_insert_attrib(confirm, "name", "Confirm AIM account"); iks_insert_attrib(confirm, "jid", client->user); iks_insert_node(iq, query); iks_insert_node(query, confirm); iks_send(client->p, iq); } else { ast_log(LOG_ERROR, "Out of memory.\n"); } if (iq) iks_delete(iq); if (query) iks_delete(query); if (confirm) iks_delete(confirm); } else if (pak->subtype == IKS_TYPE_GET && !strcasecmp(node, "confirmaccount")) { iks *iq, *query, *feature; iq = iks_new("iq"); query = iks_new("query"); feature = iks_new("feature"); if (iq && query && feature && client) { iks_insert_attrib(iq, "from", client->user); iks_insert_attrib(iq, "to", pak->from->full); iks_insert_attrib(iq, "id", pak->id); iks_insert_attrib(iq, "type", "result"); iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info"); iks_insert_attrib(feature, "var", "http://jabber.org/protocol/commands"); iks_insert_node(iq, query); iks_insert_node(query, feature); iks_send(client->p, iq); } else { ast_log(LOG_ERROR, "Out of memory.\n"); } if (iq) iks_delete(iq); if (query) iks_delete(query); if (feature) iks_delete(feature); } ASTOBJ_UNREF(client, aji_client_destroy); return IKS_FILTER_EAT; } /*! * \brief Handles tags. * \param client structure and the iq node. * \return void. */ static void aji_handle_iq(struct aji_client *client, iks *node) { /*Nothing to see here */ } /*! * \brief Handles presence packets. * \param client structure and the node. * \return void. */ static void aji_handle_message(struct aji_client *client, ikspak *pak) { struct aji_message *insert, *tmp; int flag = 0; if (!(insert = ast_calloc(1, sizeof(struct aji_message)))) return; time(&insert->arrived); if (iks_find_cdata(pak->x, "body")) insert->message = ast_strdup(iks_find_cdata(pak->x, "body")); if(pak->id) ast_copy_string(insert->id, pak->id, sizeof(insert->message)); if (pak->from) insert->from = ast_strdup(pak->from->full); AST_LIST_LOCK(&client->messages); AST_LIST_TRAVERSE_SAFE_BEGIN(&client->messages, tmp, list) { if (flag) { AST_LIST_REMOVE_CURRENT(&client->messages, list); if (tmp->from) free(tmp->from); if (tmp->message) free(tmp->message); } else if (difftime(time(NULL), tmp->arrived) >= client->message_timeout) { flag = 1; AST_LIST_REMOVE_CURRENT(&client->messages, list); if (tmp->from) free(tmp->from); if (tmp->message) free(tmp->message); } } AST_LIST_TRAVERSE_SAFE_END; AST_LIST_INSERT_HEAD(&client->messages, insert, list); AST_LIST_UNLOCK(&client->messages); } static void aji_handle_presence(struct aji_client *client, ikspak *pak) { int status, priority; struct aji_buddy *buddy; struct aji_resource *tmp = NULL, *last = NULL, *found = NULL; char *ver, *node, *descrip, *type; if(client->state != AJI_CONNECTED) aji_create_buddy(pak->from->partial, client); buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial); if (!buddy && pak->from->partial) { /* allow our jid to be used to log in with another resource */ if (!strcmp((const char *)pak->from->partial, (const char *)client->jid->partial)) aji_create_buddy(pak->from->partial, client); else ast_log(LOG_NOTICE, "Got presence packet from %s, someone not in our roster!!!!\n", pak->from->partial); return; } type = iks_find_attrib(pak->x, "type"); if(client->component && type &&!strcasecmp("probe", type)) { aji_set_presence(client, pak->from->full, iks_find_attrib(pak->x, "to"), 1, client->statusmessage); ast_verbose("what i was looking for \n"); } ASTOBJ_WRLOCK(buddy); status = (pak->show) ? pak->show : 6; priority = atoi((iks_find_cdata(pak->x, "priority")) ? iks_find_cdata(pak->x, "priority") : "0"); tmp = buddy->resources; descrip = ast_strdup(iks_find_cdata(pak->x,"status")); while (tmp && pak->from->resource) { if (!strcasecmp(tmp->resource, pak->from->resource)) { tmp->status = status; if (tmp->description) free(tmp->description); tmp->description = descrip; found = tmp; if (status == 6) { /* Sign off Destroy resource */ if (last && found->next) { last->next = found->next; } else if (!last) { if (found->next) buddy->resources = found->next; else buddy->resources = NULL; } else if (!found->next) { if (last) last->next = NULL; else buddy->resources = NULL; } free(found); found = NULL; break; } /* resource list is sorted by descending priority */ if (tmp->priority != priority) { found->priority = priority; if (!last && !found->next) /* resource was found to be unique, leave loop */ break; /* search for resource in our list and take it out for the moment */ if (last) last->next = found->next; else buddy->resources = found->next; last = NULL; tmp = buddy->resources; if (!buddy->resources) buddy->resources = found; /* priority processing */ while (tmp) { /* insert resource back according to its priority value */ if (found->priority > tmp->priority) { if (last) /* insert within list */ last->next = found; found->next = tmp; if (!last) /* insert on top */ buddy->resources = found; break; } if (!tmp->next) { /* insert at the end of the list */ tmp->next = found; found->next = NULL; break; } last = tmp; tmp = tmp->next; } } break; } last = tmp; tmp = tmp->next; } /* resource not found in our list, create it */ if (!found && status != 6 && pak->from->resource) { found = (struct aji_resource *) malloc(sizeof(struct aji_resource)); memset(found, 0, sizeof(struct aji_resource)); if (!found) { ast_log(LOG_ERROR, "Out of memory!\n"); return; } ast_copy_string(found->resource, pak->from->resource, sizeof(found->resource)); found->status = status; found->description = descrip; found->priority = priority; found->next = NULL; last = NULL; tmp = buddy->resources; while (tmp) { if (found->priority > tmp->priority) { if (last) last->next = found; found->next = tmp; if (!last) buddy->resources = found; break; } if (!tmp->next) { tmp->next = found; break; } last = tmp; tmp = tmp->next; } if (!tmp) buddy->resources = found; } ASTOBJ_UNLOCK(buddy); ASTOBJ_UNREF(buddy, aji_buddy_destroy); node = iks_find_attrib(iks_find(pak->x, "c"), "node"); ver = iks_find_attrib(iks_find(pak->x, "c"), "ver"); /* handle gmail client's special caps:c tag */ if (!node && !ver) { node = iks_find_attrib(iks_find(pak->x, "caps:c"), "node"); ver = iks_find_attrib(iks_find(pak->x, "caps:c"), "ver"); } /* retrieve capabilites of the new resource */ if(status !=6 && found && !found->cap) { found->cap = aji_find_version(node, ver, pak); if(gtalk_yuck(pak->x)) /* gtalk should do discover */ found->cap->jingle = 1; if(found->cap->jingle && option_debug > 4) ast_log(LOG_DEBUG,"Special case for google till they support discover.\n"); else { iks *iq, *query; iq = iks_new("iq"); query = iks_new("query"); if(query && iq) { iks_insert_attrib(iq, "type", "get"); iks_insert_attrib(iq, "to", pak->from->full); iks_insert_attrib(iq,"from", client->jid->full); iks_insert_attrib(iq, "id", client->mid); ast_aji_increment_mid(client->mid); iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info"); iks_insert_node(iq, query); iks_send(client->p, iq); } else ast_log(LOG_ERROR, "Out of memory.\n"); if(query) iks_delete(query); if(iq) iks_delete(iq); } } if (option_verbose > 4) { switch (pak->subtype) { case IKS_TYPE_AVAILABLE: ast_verbose(VERBOSE_PREFIX_3 "JABBER: I am available ^_* %i\n", pak->subtype); break; case IKS_TYPE_UNAVAILABLE: ast_verbose(VERBOSE_PREFIX_3 "JABBER: I am unavailable ^_* %i\n", pak->subtype); break; default: ast_verbose(VERBOSE_PREFIX_3 "JABBER: Ohh sexy and the wrong type: %i\n", pak->subtype); } switch (pak->show) { case IKS_SHOW_UNAVAILABLE: ast_verbose(VERBOSE_PREFIX_3 "JABBER: type: %i subtype %i\n", pak->subtype, pak->show); break; case IKS_SHOW_AVAILABLE: ast_verbose(VERBOSE_PREFIX_3 "JABBER: type is available\n"); break; case IKS_SHOW_CHAT: ast_verbose(VERBOSE_PREFIX_3 "JABBER: type: %i subtype %i\n", pak->subtype, pak->show); break; case IKS_SHOW_AWAY: ast_verbose(VERBOSE_PREFIX_3 "JABBER: type is away\n"); break; case IKS_SHOW_XA: ast_verbose(VERBOSE_PREFIX_3 "JABBER: type: %i subtype %i\n", pak->subtype, pak->show); break; case IKS_SHOW_DND: ast_verbose(VERBOSE_PREFIX_3 "JABBER: type: %i subtype %i\n", pak->subtype, pak->show); break; default: ast_verbose(VERBOSE_PREFIX_3 "JABBER: Kinky! how did that happen %i\n", pak->show); } } } /*! * \brief handles subscription requests. * \param aji_client struct and xml packet. * \return void. */ static void aji_handle_subscribe(struct aji_client *client, ikspak *pak) { iks *presence = NULL, *status = NULL; struct aji_buddy* buddy = NULL; switch (pak->subtype) { case IKS_TYPE_SUBSCRIBE: presence = iks_new("presence"); status = iks_new("status"); if(presence && status) { iks_insert_attrib(presence, "type", "subscribed"); iks_insert_attrib(presence, "to", pak->from->full); iks_insert_attrib(presence, "from", client->jid->full); if(pak->id) iks_insert_attrib(presence, "id", pak->id); iks_insert_cdata(status, "Asterisk has approved subscription", 0); iks_insert_node(presence, status); iks_send(client->p, presence); } else ast_log(LOG_ERROR, "Unable to allocate nodes\n"); if(presence) iks_delete(presence); if(status) iks_delete(status); if(client->component) aji_set_presence(client, pak->from->full, iks_find_attrib(pak->x, "to"), 1, client->statusmessage); case IKS_TYPE_SUBSCRIBED: buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial); if (!buddy && pak->from->partial) { aji_create_buddy(pak->from->partial, client); } default: if (option_verbose > 4) { ast_verbose(VERBOSE_PREFIX_3 "JABBER: This is a subcription of type %i\n", pak->subtype); } } } /*! * \brief sends messages. * \param aji_client struct , reciever, message. * \return 1. */ int ast_aji_send(struct aji_client *client, const char *address, const char *message) { int res = 0; iks *message_packet = NULL; if (client->state == AJI_CONNECTED) { message_packet = iks_make_msg(IKS_TYPE_CHAT, address, message); if (message_packet) { iks_insert_attrib(message_packet, "from", client->jid->full); res = iks_send(client->p, message_packet); } else { ast_log(LOG_ERROR, "Out of memory.\n"); } if (message_packet) iks_delete(message_packet); } else ast_log(LOG_WARNING, "JABBER: Not connected can't send\n"); return 1; } /*! * \brief create a chatroom. * \param aji_client struct , room, server, topic for the room. * \return 0. */ int ast_aji_create_chat(struct aji_client *client, char *room, char *server, char *topic) { int res = 0; iks *iq = NULL; iq = iks_new("iq"); if (iq && client) { iks_insert_attrib(iq, "type", "get"); iks_insert_attrib(iq, "to", server); iks_insert_attrib(iq, "id", client->mid); ast_aji_increment_mid(client->mid); iks_send(client->p, iq); } else ast_log(LOG_ERROR, "Out of memory.\n"); iks_delete(iq); return res; } /*! * \brief join a chatroom. * \param aji_client struct , room. * \return res. */ int ast_aji_join_chat(struct aji_client *client, char *room) { int res = 0; iks *presence = NULL, *priority = NULL; presence = iks_new("presence"); priority = iks_new("priority"); if (presence && priority && client) { iks_insert_cdata(priority, "0", 1); iks_insert_attrib(presence, "to", room); iks_insert_node(presence, priority); res = iks_send(client->p, presence); iks_insert_cdata(priority, "5", 1); iks_insert_attrib(presence, "to", room); res = iks_send(client->p, presence); } else ast_log(LOG_ERROR, "Out of memory.\n"); if (presence) iks_delete(presence); if (priority) iks_delete(priority); return res; } /*! * \brief invite to a chatroom. * \param aji_client struct ,user, room, message. * \return res. */ int ast_aji_invite_chat(struct aji_client *client, char *user, char *room, char *message) { int res = 0; iks *invite, *body, *namespace; invite = iks_new("message"); body = iks_new("body"); namespace = iks_new("x"); if (client && invite && body && namespace) { iks_insert_attrib(invite, "to", user); iks_insert_attrib(invite, "id", client->mid); ast_aji_increment_mid(client->mid); iks_insert_cdata(body, message, 0); iks_insert_attrib(namespace, "xmlns", "jabber:x:conference"); iks_insert_attrib(namespace, "jid", room); iks_insert_node(invite, body); iks_insert_node(invite, namespace); res = iks_send(client->p, invite); } else ast_log(LOG_ERROR, "Out of memory.\n"); if (body) iks_delete(body); if (namespace) iks_delete(namespace); if (invite) iks_delete(invite); return res; } /*! * \brief receive message loop. * \param aji_client struct. * \return void. */ static void *aji_recv_loop(void *data) { struct aji_client *client = ASTOBJ_REF((struct aji_client *) data); int res = IKS_HOOK; do { if (res != IKS_OK) { while(res != IKS_OK) { if(option_verbose > 3) ast_verbose("JABBER: reconnecting.\n"); res = aji_reconnect(client); sleep(4); } } res = iks_recv(client->p, 1); if (client->state == AJI_DISCONNECTING) { if (option_debug > 1) ast_log(LOG_DEBUG, "Ending our Jabber client's thread due to a disconnect\n"); pthread_exit(NULL); } client->timeout--; if (res == IKS_HOOK) ast_log(LOG_WARNING, "JABBER: Got hook event.\n"); else if (res == IKS_NET_TLSFAIL) ast_log(LOG_WARNING, "JABBER: Failure in TLS.\n"); else if (client->timeout == 0 && client->state == AJI_CONNECTED) { res = client->keepalive ? iks_send_raw(client->p, " ") : IKS_OK; if(res == IKS_OK) client->timeout = 50; else ast_log(LOG_WARNING, "JABBER: Network Timeout\n"); } else if (res == IKS_NET_RWERR) ast_log(LOG_WARNING, "JABBER: socket read error\n"); } while (client); ASTOBJ_UNREF(client, aji_client_destroy); return 0; } /*! * \brief increments the mid field for messages and other events. * \param message id. * \return void. */ void ast_aji_increment_mid(char *mid) { int i = 0; for (i = strlen(mid) - 1; i >= 0; i--) { if (mid[i] != 'z') { mid[i] = mid[i] + 1; i = 0; } else mid[i] = 'a'; } } /*! * \brief attempts to register to a transport. * \param aji_client struct, and xml packet. * \return IKS_FILTER_EAT. */ /*allows for registering to transport , was too sketch and is out for now. */ /*static int aji_register_transport(void *data, ikspak *pak) { struct aji_client *client = ASTOBJ_REF((struct aji_client *) data); int res = 0; struct aji_buddy *buddy = NULL; iks *send = iks_make_iq(IKS_TYPE_GET, "jabber:iq:register"); if (client && send) { ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, { ASTOBJ_RDLOCK(iterator); if (iterator->btype == AJI_TRANS) { buddy = iterator; } ASTOBJ_UNLOCK(iterator); }); iks_filter_remove_hook(client->f, aji_register_transport); iks_filter_add_rule(client->f, aji_register_transport2, client, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_NS, IKS_NS_REGISTER, IKS_RULE_DONE); iks_insert_attrib(send, "to", buddy->host); iks_insert_attrib(send, "id", client->mid); ast_aji_increment_mid(client->mid); iks_insert_attrib(send, "from", client->user); res = iks_send(client->p, send); } else ast_log(LOG_ERROR, "Out of memory.\n"); if (send) iks_delete(send); ASTOBJ_UNREF(client, aji_client_destroy); return IKS_FILTER_EAT; } */ /*! * \brief attempts to register to a transport step 2. * \param aji_client struct, and xml packet. * \return IKS_FILTER_EAT. */ /* more of the same blob of code, too wonky for now*/ /* static int aji_register_transport2(void *data, ikspak *pak) { struct aji_client *client = ASTOBJ_REF((struct aji_client *) data); int res = 0; struct aji_buddy *buddy = NULL; iks *regiq = iks_new("iq"); iks *regquery = iks_new("query"); iks *reguser = iks_new("username"); iks *regpass = iks_new("password"); if (client && regquery && reguser && regpass && regiq) { ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, { ASTOBJ_RDLOCK(iterator); if (iterator->btype == AJI_TRANS) buddy = iterator; ASTOBJ_UNLOCK(iterator); }); iks_filter_remove_hook(client->f, aji_register_transport2); iks_insert_attrib(regiq, "to", buddy->host); iks_insert_attrib(regiq, "type", "set"); iks_insert_attrib(regiq, "id", client->mid); ast_aji_increment_mid(client->mid); iks_insert_attrib(regiq, "from", client->user); iks_insert_attrib(regquery, "xmlns", "jabber:iq:register"); iks_insert_cdata(reguser, buddy->user, 0); iks_insert_cdata(regpass, buddy->pass, 0); iks_insert_node(regiq, regquery); iks_insert_node(regquery, reguser); iks_insert_node(regquery, regpass); res = iks_send(client->p, regiq); } else ast_log(LOG_ERROR, "Out of memory.\n"); if (regiq) iks_delete(regiq); if (regquery) iks_delete(regquery); if (reguser) iks_delete(reguser); if (regpass) iks_delete(regpass); ASTOBJ_UNREF(client, aji_client_destroy); return IKS_FILTER_EAT; } */ /*! * \brief goes through roster and prunes users not needed in list, or adds them accordingly. * \param aji_client struct. * \return void. */ static void aji_pruneregister(struct aji_client *client) { int res = 0; iks *removeiq = iks_new("iq"); iks *removequery = iks_new("query"); iks *removeitem = iks_new("item"); iks *send = iks_make_iq(IKS_TYPE_GET, "http://jabber.org/protocol/disco#items"); if (client && removeiq && removequery && removeitem && send) { iks_insert_node(removeiq, removequery); iks_insert_node(removequery, removeitem); ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, { ASTOBJ_RDLOCK(iterator); /* For an aji_buddy, both AUTOPRUNE and AUTOREGISTER will never * be called at the same time */ if (ast_test_flag(iterator, AJI_AUTOPRUNE)) { res = iks_send(client->p, iks_make_s10n(IKS_TYPE_UNSUBSCRIBE, iterator->name, "GoodBye your status is no longer needed by Asterisk the Open Source PBX" " so I am no longer subscribing to your presence.\n")); res = iks_send(client->p, iks_make_s10n(IKS_TYPE_UNSUBSCRIBED, iterator->name, "GoodBye you are no longer in the asterisk config file so I am removing" " your access to my presence.\n")); iks_insert_attrib(removeiq, "from", client->jid->full); iks_insert_attrib(removeiq, "type", "set"); iks_insert_attrib(removequery, "xmlns", "jabber:iq:roster"); iks_insert_attrib(removeitem, "jid", iterator->name); iks_insert_attrib(removeitem, "subscription", "remove"); res = iks_send(client->p, removeiq); } else if (ast_test_flag(iterator, AJI_AUTOREGISTER)) { res = iks_send(client->p, iks_make_s10n(IKS_TYPE_SUBSCRIBE, iterator->name, "Greetings I am the Asterisk Open Source PBX and I want to subscribe to your presence\n")); ast_clear_flag(iterator, AJI_AUTOREGISTER); } ASTOBJ_UNLOCK(iterator); }); } else ast_log(LOG_ERROR, "Out of memory.\n"); if (removeiq) iks_delete(removeiq); if (removequery) iks_delete(removequery); if (removeitem) iks_delete(removeitem); if (send) iks_delete(send); ASTOBJ_CONTAINER_PRUNE_MARKED(&client->buddies, aji_buddy_destroy); } /*! * \brief filters the roster packet we get back from server. * \param aji_client struct, and xml packet. * \return IKS_FILTER_EAT. */ static int aji_filter_roster(void *data, ikspak *pak) { struct aji_client *client = ASTOBJ_REF((struct aji_client *) data); int flag = 0; iks *x = NULL; struct aji_buddy *buddy; client->state = AJI_CONNECTED; ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, { ASTOBJ_RDLOCK(iterator); x = iks_child(pak->query); flag = 0; while (x) { if (!iks_strcmp(iks_name(x), "item")) { if (!strcasecmp(iterator->name, iks_find_attrib(x, "jid"))) { flag = 1; ast_clear_flag(iterator, AJI_AUTOPRUNE | AJI_AUTOREGISTER); } } x = iks_next(x); } if (!flag) ast_copy_flags(iterator, client, AJI_AUTOREGISTER); if (x) iks_delete(x); ASTOBJ_UNLOCK(iterator); }); x = iks_child(pak->query); while (x) { flag = 0; if (iks_strcmp(iks_name(x), "item") == 0) { ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, { ASTOBJ_RDLOCK(iterator); if (!strcasecmp(iterator->name, iks_find_attrib(x, "jid"))) flag = 1; ASTOBJ_UNLOCK(iterator); }); if (!flag) { buddy = (struct aji_buddy *) malloc(sizeof(struct aji_buddy)); if (!buddy) { ast_log(LOG_WARNING, "Out of memory\n"); return 0; } memset(buddy, 0, sizeof(struct aji_buddy)); ASTOBJ_INIT(buddy); ASTOBJ_WRLOCK(buddy); ast_copy_string(buddy->name, iks_find_attrib(x, "jid"), sizeof(buddy->name)); ast_clear_flag(buddy, AST_FLAGS_ALL); if(ast_test_flag(client, AJI_AUTOPRUNE)) { ast_set_flag(buddy, AJI_AUTOPRUNE); buddy->objflags |= ASTOBJ_FLAG_MARKED; } else ast_set_flag(buddy, AJI_AUTOREGISTER); ASTOBJ_UNLOCK(buddy); if (buddy) { ASTOBJ_CONTAINER_LINK(&client->buddies, buddy); ASTOBJ_UNREF(buddy, aji_buddy_destroy); } } } x = iks_next(x); } if (x) iks_delete(x); aji_pruneregister(client); ASTOBJ_UNREF(client, aji_client_destroy); return IKS_FILTER_EAT; } static int aji_reconnect(struct aji_client *client) { int res = 0; if (client->state) client->state = AJI_DISCONNECTED; client->timeout=50; if (client->p) iks_parser_reset(client->p); if (client->authorized) client->authorized = 0; if(client->component) res = aji_component_initialize(client); else res = aji_client_initialize(client); return res; } static int aji_get_roster(struct aji_client *client) { iks *roster = NULL; roster = iks_make_iq(IKS_TYPE_GET, IKS_NS_ROSTER); if(roster) { iks_insert_attrib(roster, "id", "roster"); aji_set_presence(client, NULL, client->jid->full, 1, client->statusmessage); iks_send(client->p, roster); } if (roster) iks_delete(roster); return 1; } /*! * \brief connects as a client to jabber server. * \param aji_client struct, and xml packet. * \return res. */ static int aji_client_connect(void *data, ikspak *pak) { struct aji_client *client = ASTOBJ_REF((struct aji_client *) data); int res = 0; if (client) { if (client->state == AJI_DISCONNECTED) { iks_filter_add_rule(client->f, aji_filter_roster, client, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_ID, "roster", IKS_RULE_DONE); client->state = AJI_CONNECTING; client->jid = (iks_find_cdata(pak->query, "jid")) ? iks_id_new(client->stack, iks_find_cdata(pak->query, "jid")) : client->jid; iks_filter_remove_hook(client->f, aji_client_connect); if(!client->component) /*client*/ aji_get_roster(client); } } else ast_log(LOG_ERROR, "Out of memory.\n"); ASTOBJ_UNREF(client, aji_client_destroy); return res; } /*! * \brief prepares client for connect. * \param aji_client struct. * \return 1. */ static int aji_client_initialize(struct aji_client *client) { int connected = 0; connected = iks_connect_via(client->p, S_OR(client->serverhost, client->jid->server), client->port, client->jid->server); if (connected == IKS_NET_NOCONN) { ast_log(LOG_ERROR, "JABBER ERROR: No Connection\n"); return IKS_HOOK; } else if (connected == IKS_NET_NODNS) { ast_log(LOG_ERROR, "JABBER ERROR: No DNS %s for client to %s\n", client->name, S_OR(client->serverhost, client->jid->server)); return IKS_HOOK; } else iks_recv(client->p, 30); return IKS_OK; } /*! * \brief prepares component for connect. * \param aji_client struct. * \return 1. */ static int aji_component_initialize(struct aji_client *client) { int connected = 1; connected = iks_connect_via(client->p, S_OR(client->serverhost, client->jid->server), client->port, client->user); if (connected == IKS_NET_NOCONN) { ast_log(LOG_ERROR, "JABBER ERROR: No Connection\n"); return IKS_HOOK; } else if (connected == IKS_NET_NODNS) { ast_log(LOG_ERROR, "JABBER ERROR: No DNS %s for client to %s\n", client->name, S_OR(client->serverhost, client->jid->server)); return IKS_HOOK; } else if (!connected) iks_recv(client->p, 30); return IKS_OK; } /*! * \brief disconnect from jabber server. * \param aji_client struct. * \return 1. */ int ast_aji_disconnect(struct aji_client *client) { if (client) { if (option_verbose > 3) ast_verbose(VERBOSE_PREFIX_3 "JABBER: Disconnecting\n"); iks_disconnect(client->p); iks_parser_delete(client->p); ASTOBJ_UNREF(client, aji_client_destroy); } return 1; } /*! * \brief set presence of client. * \param aji_client struct, user to send it to, and from, level, description. * \return void. */ static void aji_set_presence(struct aji_client *client, char *to, char *from, int level, char *desc) { int res = 0; iks *presence = iks_make_pres(level, desc); iks *cnode = iks_new("c"); iks *priority = iks_new("priority"); iks_insert_cdata(priority, "0", 1); if (presence && cnode && client) { if(to) iks_insert_attrib(presence, "to", to); if(from) iks_insert_attrib(presence, "from", from); iks_insert_attrib(cnode, "node", "http://www.asterisk.org/xmpp/client/caps"); iks_insert_attrib(cnode, "ver", "asterisk-xmpp"); iks_insert_attrib(cnode, "ext", "voice-v1"); iks_insert_attrib(cnode, "xmlns", "http://jabber.org/protocol/caps"); iks_insert_node(presence, cnode); res = iks_send(client->p, presence); } else ast_log(LOG_ERROR, "Out of memory.\n"); if (cnode) iks_delete(cnode); if (presence) iks_delete(presence); } /*! * \brief turnon console debugging. * \param fd, number of args, args. * \return RESULT_SUCCESS. */ static int aji_do_debug(int fd, int argc, char *argv[]) { ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, { ASTOBJ_RDLOCK(iterator); iterator->debug = 1; ASTOBJ_UNLOCK(iterator); }); ast_cli(fd, "Jabber Debugging Enabled.\n"); return RESULT_SUCCESS; } /*! * \brief reload jabber module. * \param fd, number of args, args. * \return RESULT_SUCCESS. */ static int aji_do_reload(int fd, int argc, char *argv[]) { aji_reload(); ast_cli(fd, "Jabber Reloaded.\n"); return RESULT_SUCCESS; } /*! * \brief turnoff console debugging. * \param fd, number of args, args. * \return RESULT_SUCCESS. */ static int aji_no_debug(int fd, int argc, char *argv[]) { ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, { ASTOBJ_RDLOCK(iterator); iterator->debug = 0; ASTOBJ_UNLOCK(iterator); }); ast_cli(fd, "Jabber Debugging Disabled.\n"); return RESULT_SUCCESS; } /*! * \brief show client status. * \param fd, number of args, args. * \return RESULT_SUCCESS. */ static int aji_show_clients(int fd, int argc, char *argv[]) { char *status; int count = 0; ast_cli(fd, "Jabber Users and their status:\n"); ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, { ASTOBJ_RDLOCK(iterator); count++; switch (iterator->state) { case AJI_DISCONNECTED: status = "Disconnected"; break; case AJI_CONNECTING: status = "Connecting"; break; case AJI_CONNECTED: status = "Connected"; break; default: status = "Unknown"; } ast_cli(fd, " User: %s - %s\n", iterator->user, status); ASTOBJ_UNLOCK(iterator); }); ast_cli(fd, "----\n"); ast_cli(fd, " Number of users: %d\n", count); return RESULT_SUCCESS; } /*! * \brief send test message for debugging. * \param fd, number of args, args. * \return RESULT_SUCCESS. */ static int aji_test(int fd, int argc, char *argv[]) { struct aji_client *client; struct aji_resource *resource; const char *name = "asterisk"; struct aji_message *tmp; if (argc > 3) return RESULT_SHOWUSAGE; else if (argc == 3) name = argv[2]; if (!(client = ASTOBJ_CONTAINER_FIND(&clients, name))) { ast_cli(fd, "Unable to find client '%s'!\n", name); return RESULT_FAILURE; } /* XXX Does Matt really want everyone to use his personal address for tests? */ /* XXX yes he does */ ast_aji_send(client, "mogorman@astjab.org", "blahblah"); ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, { ASTOBJ_RDLOCK(iterator); ast_verbose("User: %s\n", iterator->name); for (resource = iterator->resources; resource; resource = resource->next) { ast_verbose("Resource: %s\n", resource->resource); if(resource->cap) { ast_verbose(" client: %s\n", resource->cap->parent->node); ast_verbose(" version: %s\n", resource->cap->version); ast_verbose(" Jingle Capable: %d\n", resource->cap->jingle); } ast_verbose(" Priority: %d\n", resource->priority); ast_verbose(" Status: %d\n", resource->status); ast_verbose(" Message: %s\n", S_OR(resource->description,"")); } ASTOBJ_UNLOCK(iterator); }); ast_verbose("\nOooh a working message stack!\n"); AST_LIST_LOCK(&client->messages); AST_LIST_TRAVERSE(&client->messages, tmp, list) { ast_verbose(" Message from: %s with id %s @ %s %s\n",tmp->from, S_OR(tmp->id,""), ctime(&tmp->arrived), S_OR(tmp->message, "")); } AST_LIST_UNLOCK(&client->messages); ASTOBJ_UNREF(client, aji_client_destroy); return RESULT_SUCCESS; } /*! * \brief creates aji_client structure. * \param label, ast_variable, debug, pruneregister, component/client, aji_client to dump into. * \return 0. */ static int aji_create_client(char *label, struct ast_variable *var, int debug) { char *resource; struct aji_client *client = NULL; int flag = 0; client = ASTOBJ_CONTAINER_FIND(&clients,label); if (!client) { flag = 1; client = (struct aji_client *) malloc(sizeof(struct aji_client)); if (!client) { ast_log(LOG_ERROR, "Out of memory!\n"); return 0; } memset(client, 0, sizeof(struct aji_client)); ASTOBJ_INIT(client); ASTOBJ_WRLOCK(client); ASTOBJ_CONTAINER_INIT(&client->buddies); } else { ASTOBJ_WRLOCK(client); ASTOBJ_UNMARK(client); } ASTOBJ_CONTAINER_MARKALL(&client->buddies); ast_copy_string(client->name, label, sizeof(client->name)); ast_copy_string(client->mid, "aaaaa", sizeof(client->mid)); /* Set default values for the client object */ client->debug = debug; ast_copy_flags(client, &globalflags, AST_FLAGS_ALL); client->port = 5222; client->usetls = 1; client->usesasl = 1; client->forcessl = 0; client->keepalive = 1; client->timeout = 50; client->message_timeout = 100; AST_LIST_HEAD_INIT(&client->messages); client->component = 0; ast_copy_string(client->statusmessage, "Online and Available", sizeof(client->statusmessage)); if (flag) { client->authorized = 0; client->state = AJI_DISCONNECTED; } while (var) { if (!strcasecmp(var->name, "username")) ast_copy_string(client->user, var->value, sizeof(client->user)); else if (!strcasecmp(var->name, "serverhost")) ast_copy_string(client->serverhost, var->value, sizeof(client->serverhost)); else if (!strcasecmp(var->name, "secret")) ast_copy_string(client->password, var->value, sizeof(client->password)); else if (!strcasecmp(var->name, "statusmessage")) ast_copy_string(client->statusmessage, var->value, sizeof(client->statusmessage)); else if (!strcasecmp(var->name, "port")) client->port = atoi(var->value); else if (!strcasecmp(var->name, "timeout")) client->message_timeout = atoi(var->value); else if (!strcasecmp(var->name, "debug")) client->debug = (ast_false(var->value)) ? 0 : 1; else if (!strcasecmp(var->name, "type")) { if (!strcasecmp(var->value, "component")) client->component = 1; } else if (!strcasecmp(var->name, "usetls")) { client->usetls = (ast_false(var->value)) ? 0 : 1; } else if (!strcasecmp(var->name, "usesasl")) { client->usesasl = (ast_false(var->value)) ? 0 : 1; } else if (!strcasecmp(var->name, "forceoldssl")) client->forcessl = (ast_false(var->value)) ? 0 : 1; else if (!strcasecmp(var->name, "keepalive")) client->keepalive = (ast_false(var->value)) ? 0 : 1; else if (!strcasecmp(var->name, "autoprune")) ast_set2_flag(client, ast_true(var->value), AJI_AUTOPRUNE); else if (!strcasecmp(var->name, "autoregister")) ast_set2_flag(client, ast_true(var->value), AJI_AUTOREGISTER); else if (!strcasecmp(var->name, "buddy")) aji_create_buddy(var->value, client); /* no transport support in this version */ /* else if (!strcasecmp(var->name, "transport")) aji_create_transport(var->value, client); */ var = var->next; } if (!flag) { ASTOBJ_UNLOCK(client); ASTOBJ_UNREF(client, aji_client_destroy); return 1; } client->p = iks_stream_new(((client->component) ? "jabber:component:accept" : "jabber:client"), client, aji_act_hook); if (!client->p) { ast_log(LOG_ERROR, "Failed to create stream for client '%s'!\n", client->name); return 0; } client->stack = iks_stack_new(8192, 8192); if (!client->stack) { ast_log(LOG_ERROR, "Failed to allocate stack for client '%s'\n", client->name); return 0; } client->f = iks_filter_new(); if (!client->f) { ast_log(LOG_ERROR, "Failed to create filter for client '%s'\n", client->name); return 0; } if (!strchr(client->user, '/') && !client->component) { /*client */ resource = NULL; asprintf(&resource, "%s/asterisk", client->user); if (resource) { client->jid = iks_id_new(client->stack, resource); free(resource); } } else client->jid = iks_id_new(client->stack, client->user); if (client->component) { iks_filter_add_rule(client->f, aji_dinfo_handler, client, IKS_RULE_NS, "http://jabber.org/protocol/disco#info", IKS_RULE_DONE); iks_filter_add_rule(client->f, aji_ditems_handler, client, IKS_RULE_NS, "http://jabber.org/protocol/disco#items", IKS_RULE_DONE); iks_filter_add_rule(client->f, aji_register_query_handler, client, IKS_RULE_SUBTYPE, IKS_TYPE_GET, IKS_RULE_NS, "jabber:iq:register", IKS_RULE_DONE); iks_filter_add_rule(client->f, aji_register_approve_handler, client, IKS_RULE_SUBTYPE, IKS_TYPE_SET, IKS_RULE_NS, "jabber:iq:register", IKS_RULE_DONE); } else { iks_filter_add_rule(client->f, aji_client_info_handler, client, IKS_RULE_NS, "http://jabber.org/protocol/disco#info", IKS_RULE_DONE); } if (!strchr(client->user, '/') && !client->component) { /*client */ resource = NULL; asprintf(&resource, "%s/asterisk", client->user); if (resource) { client->jid = iks_id_new(client->stack, resource); free(resource); } } else client->jid = iks_id_new(client->stack, client->user); iks_set_log_hook(client->p, aji_log_hook); ASTOBJ_UNLOCK(client); ASTOBJ_CONTAINER_LINK(&clients,client); return 1; } /*! * \brief creates transport. * \param label, buddy to dump it into. * \return 0. */ /* no connecting to transports today */ /* static int aji_create_transport(char *label, struct aji_client *client) { char *server = NULL, *buddyname = NULL, *user = NULL, *pass = NULL; struct aji_buddy *buddy = NULL; buddy = ASTOBJ_CONTAINER_FIND(&client->buddies,label); if (!buddy) { buddy = malloc(sizeof(struct aji_buddy)); if(!buddy) { ast_log(LOG_WARNING, "Out of memory\n"); return 0; } memset(buddy, 0, sizeof(struct aji_buddy)); ASTOBJ_INIT(buddy); } ASTOBJ_WRLOCK(buddy); server = label; if ((buddyname = strchr(label, ','))) { *buddyname = '\0'; buddyname++; if (buddyname && buddyname[0] != '\0') { if ((user = strchr(buddyname, ','))) { *user = '\0'; user++; if (user && user[0] != '\0') { if ((pass = strchr(user, ','))) { *pass = '\0'; pass++; ast_copy_string(buddy->pass, pass, sizeof(buddy->pass)); ast_copy_string(buddy->user, user, sizeof(buddy->user)); ast_copy_string(buddy->name, buddyname, sizeof(buddy->name)); ast_copy_string(buddy->server, server, sizeof(buddy->server)); return 1; } } } } } ASTOBJ_UNLOCK(buddy); ASTOBJ_UNMARK(buddy); ASTOBJ_CONTAINER_LINK(&client->buddies, buddy); return 0; } */ /*! * \brief creates buddy. * \param label, buddy to dump it into. * \return 0. */ static int aji_create_buddy(char *label, struct aji_client *client) { struct aji_buddy *buddy = NULL; int flag = 0; buddy = ASTOBJ_CONTAINER_FIND(&client->buddies,label); if (!buddy) { flag = 1; buddy = malloc(sizeof(struct aji_buddy)); if(!buddy) { ast_log(LOG_WARNING, "Out of memory\n"); return 0; } memset(buddy, 0, sizeof(struct aji_buddy)); ASTOBJ_INIT(buddy); } ASTOBJ_WRLOCK(buddy); ast_copy_string(buddy->name, label, sizeof(buddy->name)); ASTOBJ_UNLOCK(buddy); if(flag) ASTOBJ_CONTAINER_LINK(&client->buddies, buddy); else { ASTOBJ_UNMARK(buddy); ASTOBJ_UNREF(buddy, aji_buddy_destroy); } return 1; } /*! * \brief load config file. * \param void. * \return 1. */ static int aji_load_config(void) { char *cat = NULL; int debug = 1; struct ast_config *cfg = NULL; struct ast_variable *var = NULL; cfg = ast_config_load(JABBER_CONFIG); if (!cfg) { ast_log(LOG_WARNING, "No such configuration file %s\n", JABBER_CONFIG); return 0; } cat = ast_category_browse(cfg, NULL); for (var = ast_variable_browse(cfg, "general"); var; var = var->next) { if (!strcasecmp(var->name, "debug")) debug = (ast_false(ast_variable_retrieve(cfg, "general", "debug"))) ? 0 : 1; else if (!strcasecmp(var->name, "autoprune")) ast_set2_flag(&globalflags, ast_true(var->value), AJI_AUTOPRUNE); else if (!strcasecmp(var->name, "autoregister")) ast_set2_flag(&globalflags, ast_true(var->value), AJI_AUTOREGISTER); } while (cat) { if (strcasecmp(cat, "general")) { var = ast_variable_browse(cfg, cat); aji_create_client(cat, var, debug); } cat = ast_category_browse(cfg, cat); } ast_config_destroy(cfg); /* or leak memory */ return 1; } /*! * \brief grab a aji_client structure by label name or JID * (without the resource string) * \param name label or JID * \return aji_client. */ struct aji_client *ast_aji_get_client(const char *name) { struct aji_client *client = NULL; char *aux = NULL; client = ASTOBJ_CONTAINER_FIND(&clients, name); if (!client && strchr(name, '@')) { ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, { aux = ast_strdupa(iterator->user); if (strchr(aux, '/')) { /* strip resource for comparison */ aux = strsep(&aux, "/"); } if (!strncasecmp(aux, name, strlen(aux))) { client = iterator; } }); } return client; } struct aji_client_container *ast_aji_get_clients(void) { return &clients; } static char mandescr_jabber_send[] = "Description: Sends a message to a Jabber Client.\n" "Variables: \n" " Jabber: Client or transport Asterisk uses to connect to JABBER.\n" " ScreenName: User Name to message.\n" " Message: Message to be sent to the buddy\n"; /*! \brief Send a Jabber Message via call from the Manager */ static int manager_jabber_send(struct mansession *s, const struct message *m) { struct aji_client *client = NULL; const char *id = astman_get_header(m,"ActionID"); const char *jabber = astman_get_header(m,"Jabber"); const char *screenname = astman_get_header(m,"ScreenName"); const char *message = astman_get_header(m,"Message"); if (ast_strlen_zero(jabber)) { astman_send_error(s, m, "No transport specified"); return 0; } if (ast_strlen_zero(screenname)) { astman_send_error(s, m, "No ScreenName specified"); return 0; } if (ast_strlen_zero(message)) { astman_send_error(s, m, "No Message specified"); return 0; } astman_send_ack(s, m, "Attempting to send Jabber Message"); client = ast_aji_get_client(jabber); if (!client) { astman_send_error(s, m, "Could not find Sender"); return 0; } if (strchr(screenname, '@') && message){ ast_aji_send(client, screenname, message); if (!ast_strlen_zero(id)) astman_append(s, "ActionID: %s\r\n",id); astman_append(s, "Response: Success\r\n"); return 0; } if (!ast_strlen_zero(id)) astman_append(s, "ActionID: %s\r\n",id); astman_append(s, "Response: Failure\r\n"); return 0; } static int aji_reload() { ASTOBJ_CONTAINER_MARKALL(&clients); if (!aji_load_config()) { ast_log(LOG_ERROR, "JABBER: Failed to load config.\n"); return 0; } ASTOBJ_CONTAINER_PRUNE_MARKED(&clients, aji_client_destroy); ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, { ASTOBJ_RDLOCK(iterator); if(iterator->state == AJI_DISCONNECTED) { if (!iterator->thread) ast_pthread_create_background(&iterator->thread, NULL, aji_recv_loop, iterator); } else if (iterator->state == AJI_CONNECTING) aji_get_roster(iterator); ASTOBJ_UNLOCK(iterator); }); return 1; } static int unload_module(void) { /* Check if TLS is initialized. If that's the case, we can't unload this module due to a bug in the iksemel library that will cause a crash or a deadlock. We're trying to find a way to handle this, but in the meantime we will simply refuse to die... */ if (tls_initialized) { ast_log(LOG_ERROR, "Module can't be unloaded due to a bug in the Iksemel library when using TLS.\n"); return 1; /* You need a forced unload to get rid of this module */ } ast_cli_unregister_multiple(aji_cli, sizeof(aji_cli) / sizeof(struct ast_cli_entry)); ast_unregister_application(app_ajisend); ast_unregister_application(app_ajistatus); ast_manager_unregister("JabberSend"); ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, { ASTOBJ_RDLOCK(iterator); if (option_debug > 2) ast_log(LOG_DEBUG, "JABBER: Releasing and disconneing client: %s\n", iterator->name); iterator->state = AJI_DISCONNECTING; ast_aji_disconnect(iterator); pthread_join(iterator->thread, NULL); ASTOBJ_UNLOCK(iterator); }); ASTOBJ_CONTAINER_DESTROYALL(&clients, aji_client_destroy); ASTOBJ_CONTAINER_DESTROY(&clients); return 0; } static int load_module(void) { ASTOBJ_CONTAINER_INIT(&clients); if(!aji_reload()) return AST_MODULE_LOAD_DECLINE; ast_manager_register2("JabberSend", EVENT_FLAG_SYSTEM, manager_jabber_send, "Sends a message to a Jabber Client", mandescr_jabber_send); ast_register_application(app_ajisend, aji_send_exec, ajisend_synopsis, ajisend_descrip); ast_register_application(app_ajistatus, aji_status_exec, ajistatus_synopsis, ajistatus_descrip); ast_cli_register_multiple(aji_cli, sizeof(aji_cli) / sizeof(struct ast_cli_entry)); return 0; } static int reload(void) { aji_reload(); return 0; } AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "AJI - Asterisk Jabber Interface", .load = load_module, .unload = unload_module, .reload = reload, ); asterisk-1.4.21.2/res/res_clioriginate.c0000644000175000017500000001150310752076051017310 0ustar maniacmaniac/* * Asterisk -- An open source telephony toolkit. * * Copyright (C) 2005 - 2006, Digium, Inc. * * Russell Bryant * * 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 * * \brief Originate calls via the CLI * */ #include "asterisk.h" ASTERISK_FILE_VERSION(__FILE__, "$Revision: 102378 $"); #include #include #include #include "asterisk/channel.h" #include "asterisk/pbx.h" #include "asterisk/logger.h" #include "asterisk/module.h" #include "asterisk/cli.h" #include "asterisk/utils.h" #include "asterisk/frame.h" /*! The timeout for originated calls, in seconds */ #define TIMEOUT 30 static char orig_help[] = " There are two ways to use this command. A call can be originated between a\n" "channel and a specific application, or between a channel and an extension in\n" "the dialplan. This is similar to call files or the manager originate action.\n" "Calls originated with this command are given a timeout of 30 seconds.\n\n" "Usage1: originate application [appdata]\n" " This will originate a call between the specified channel tech/data and the\n" "given application. Arguments to the application are optional. If the given\n" "arguments to the application include spaces, all of the arguments to the\n" "application need to be placed in quotation marks.\n\n" "Usage2: originate extension [exten@][context]\n" " This will originate a call between the specified channel tech/data and the\n" "given extension. If no context is specified, the 'default' context will be\n" "used. If no extension is given, the 's' extension will be used.\n"; static int handle_orig(int fd, int argc, char *argv[]); static char *complete_orig(const char *line, const char *word, int pos, int state); struct ast_cli_entry cli_cliorig[] = { { { "originate", NULL }, handle_orig, "Originate a call", orig_help, complete_orig }, }; static int orig_app(int fd, const char *chan, const char *app, const char *appdata) { char *chantech; char *chandata; int reason = 0; if (ast_strlen_zero(app)) return RESULT_SHOWUSAGE; chandata = ast_strdupa(chan); chantech = strsep(&chandata, "/"); if (!chandata) { ast_cli(fd, "*** No data provided after channel type! ***\n"); return RESULT_SHOWUSAGE; } ast_pbx_outgoing_app(chantech, AST_FORMAT_SLINEAR, chandata, TIMEOUT * 1000, app, appdata, &reason, 0, NULL, NULL, NULL, NULL, NULL); return RESULT_SUCCESS; } static int orig_exten(int fd, const char *chan, const char *data) { char *chantech; char *chandata; char *exten = NULL; char *context = NULL; int reason = 0; chandata = ast_strdupa(chan); chantech = strsep(&chandata, "/"); if (!chandata) { ast_cli(fd, "*** No data provided after channel type! ***\n"); return RESULT_SHOWUSAGE; } if (!ast_strlen_zero(data)) { context = ast_strdupa(data); exten = strsep(&context, "@"); } if (ast_strlen_zero(exten)) exten = "s"; if (ast_strlen_zero(context)) context = "default"; ast_pbx_outgoing_exten(chantech, AST_FORMAT_SLINEAR, chandata, TIMEOUT * 1000, context, exten, 1, &reason, 0, NULL, NULL, NULL, NULL, NULL); return RESULT_SUCCESS; } static int handle_orig(int fd, int argc, char *argv[]) { int res; if (ast_strlen_zero(argv[1]) || ast_strlen_zero(argv[2])) return RESULT_SHOWUSAGE; /* ugly, can be removed when CLI entries have ast_module pointers */ ast_module_ref(ast_module_info->self); if (!strcasecmp("application", argv[2])) { res = orig_app(fd, argv[1], argv[3], argv[4]); } else if (!strcasecmp("extension", argv[2])) { res = orig_exten(fd, argv[1], argv[3]); } else res = RESULT_SHOWUSAGE; ast_module_unref(ast_module_info->self); return res; } static char *complete_orig(const char *line, const char *word, int pos, int state) { static char *choices[] = { "application", "extension", NULL }; char *ret; if (pos != 2) return NULL; /* ugly, can be removed when CLI entries have ast_module pointers */ ast_module_ref(ast_module_info->self); ret = ast_cli_complete(word, choices, state); ast_module_unref(ast_module_info->self); return ret; } static int unload_module(void) { ast_cli_unregister_multiple(cli_cliorig, sizeof(cli_cliorig) / sizeof(struct ast_cli_entry)); return 0; } static int load_module(void) { ast_cli_register_multiple(cli_cliorig, sizeof(cli_cliorig) / sizeof(struct ast_cli_entry)); return 0; } AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Call origination from the CLI"); asterisk-1.4.21.2/res/res_speech.c0000644000175000017500000002342610666034554016123 0ustar maniacmaniac/* * Asterisk -- An open source telephony toolkit. * * Copyright (C) 2006, Digium, Inc. * * Joshua Colp * * 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 Generic Speech Recognition API * * \author Joshua Colp */ #include "asterisk.h" ASTERISK_FILE_VERSION(__FILE__, "$Revision: 81406 $"); #include #include #include #include #include "asterisk/channel.h" #include "asterisk/module.h" #include "asterisk/lock.h" #include "asterisk/linkedlists.h" #include "asterisk/cli.h" #include "asterisk/term.h" #include "asterisk/options.h" #include "asterisk/speech.h" static AST_LIST_HEAD_STATIC(engines, ast_speech_engine); static struct ast_speech_engine *default_engine = NULL; /*! \brief Find a speech recognition engine of specified name, if NULL then use the default one */ static struct ast_speech_engine *find_engine(char *engine_name) { struct ast_speech_engine *engine = NULL; /* If no name is specified -- use the default engine */ if (engine_name == NULL || strlen(engine_name) == 0) { return default_engine; } AST_LIST_LOCK(&engines); AST_LIST_TRAVERSE_SAFE_BEGIN(&engines, engine, list) { if (!strcasecmp(engine->name, engine_name)) { break; } } AST_LIST_TRAVERSE_SAFE_END AST_LIST_UNLOCK(&engines); return engine; } /*! \brief Activate a loaded (either local or global) grammar */ int ast_speech_grammar_activate(struct ast_speech *speech, char *grammar_name) { int res = 0; if (speech->engine->activate != NULL) { res = speech->engine->activate(speech, grammar_name); } return res; } /*! \brief Deactivate a loaded grammar on a speech structure */ int ast_speech_grammar_deactivate(struct ast_speech *speech, char *grammar_name) { int res = 0; if (speech->engine->deactivate != NULL) { res = speech->engine->deactivate(speech, grammar_name); } return res; } /*! \brief Load a local grammar on a speech structure */ int ast_speech_grammar_load(struct ast_speech *speech, char *grammar_name, char *grammar) { int res = 0; if (speech->engine->load != NULL) { res = speech->engine->load(speech, grammar_name, grammar); } return res; } /*! \brief Unload a local grammar from a speech structure */ int ast_speech_grammar_unload(struct ast_speech *speech, char *grammar_name) { int res = 0; if (speech->engine->unload != NULL) { res = speech->engine->unload(speech, grammar_name); } return res; } /*! \brief Return the results of a recognition from the speech structure */ struct ast_speech_result *ast_speech_results_get(struct ast_speech *speech) { struct ast_speech_result *result = NULL; if (speech->engine->get != NULL) { result = speech->engine->get(speech); } return result; } /*! \brief Free a list of results */ int ast_speech_results_free(struct ast_speech_result *result) { struct ast_speech_result *current_result = result, *prev_result = NULL; int res = 0; while (current_result != NULL) { prev_result = current_result; /* Deallocate what we can */ if (current_result->text != NULL) { free(current_result->text); current_result->text = NULL; } if (current_result->grammar != NULL) { free(current_result->grammar); current_result->grammar = NULL; } /* Move on and then free ourselves */ current_result = current_result->next; free(prev_result); prev_result = NULL; } return res; } /*! \brief Start speech recognition on a speech structure */ void ast_speech_start(struct ast_speech *speech) { /* Clear any flags that may affect things */ ast_clear_flag(speech, AST_SPEECH_SPOKE); ast_clear_flag(speech, AST_SPEECH_QUIET); ast_clear_flag(speech, AST_SPEECH_HAVE_RESULTS); /* If results are on the structure, free them since we are starting again */ if (speech->results != NULL) { ast_speech_results_free(speech->results); speech->results = NULL; } /* If the engine needs to start stuff up, do it */ if (speech->engine->start != NULL) { speech->engine->start(speech); } return; } /*! \brief Write in signed linear audio to be recognized */ int ast_speech_write(struct ast_speech *speech, void *data, int len) { int res = 0; /* Make sure the speech engine is ready to accept audio */ if (speech->state != AST_SPEECH_STATE_READY) { return -1; } if (speech->engine->write != NULL) { speech->engine->write(speech, data, len); } return res; } /*! \brief Signal to the engine that DTMF was received */ int ast_speech_dtmf(struct ast_speech *speech, const char *dtmf) { int res = 0; if (speech->state != AST_SPEECH_STATE_READY) return -1; if (speech->engine->dtmf != NULL) { res = speech->engine->dtmf(speech, dtmf); } return res; } /*! \brief Change an engine specific attribute */ int ast_speech_change(struct ast_speech *speech, char *name, const char *value) { int res = 0; if (speech->engine->change != NULL) { res = speech->engine->change(speech, name, value); } return res; } /*! \brief Create a new speech structure using the engine specified */ struct ast_speech *ast_speech_new(char *engine_name, int format) { struct ast_speech_engine *engine = NULL; struct ast_speech *new_speech = NULL; /* Try to find the speech recognition engine that was requested */ engine = find_engine(engine_name); if (engine == NULL) { /* Invalid engine or no engine available */ return NULL; } /* Allocate our own speech structure, and try to allocate a structure from the engine too */ new_speech = ast_calloc(1, sizeof(*new_speech)); if (new_speech == NULL) { /* Ran out of memory while trying to allocate some for a speech structure */ return NULL; } /* Initialize the lock */ ast_mutex_init(&new_speech->lock); /* Make sure no results are present */ new_speech->results = NULL; /* Copy over our engine pointer */ new_speech->engine = engine; /* We are not ready to accept audio yet */ ast_speech_change_state(new_speech, AST_SPEECH_STATE_NOT_READY); /* Pass ourselves to the engine so they can set us up some more and if they error out then do not create a structure */ if (engine->create(new_speech)) { ast_mutex_destroy(&new_speech->lock); free(new_speech); new_speech = NULL; } return new_speech; } /*! \brief Destroy a speech structure */ int ast_speech_destroy(struct ast_speech *speech) { int res = 0; /* Call our engine so we are destroyed properly */ speech->engine->destroy(speech); /* Deinitialize the lock */ ast_mutex_destroy(&speech->lock); /* If results exist on the speech structure, destroy them */ if (speech->results != NULL) { ast_speech_results_free(speech->results); speech->results = NULL; } /* If a processing sound is set - free the memory used by it */ if (speech->processing_sound != NULL) { free(speech->processing_sound); speech->processing_sound = NULL; } /* Aloha we are done */ free(speech); speech = NULL; return res; } /*! \brief Change state of a speech structure */ int ast_speech_change_state(struct ast_speech *speech, int state) { int res = 0; switch (state) { case AST_SPEECH_STATE_WAIT: /* The engine heard audio, so they spoke */ ast_set_flag(speech, AST_SPEECH_SPOKE); default: speech->state = state; break; } return res; } /*! \brief Change the type of results we want */ int ast_speech_change_results_type(struct ast_speech *speech, enum ast_speech_results_type results_type) { int res = 0; speech->results_type = results_type; if (speech->engine->change_results_type) res = speech->engine->change_results_type(speech, results_type); return res; } /*! \brief Register a speech recognition engine */ int ast_speech_register(struct ast_speech_engine *engine) { struct ast_speech_engine *existing_engine = NULL; int res = 0; existing_engine = find_engine(engine->name); if (existing_engine != NULL) { /* Engine already loaded */ return -1; } if (option_verbose > 1) ast_verbose(VERBOSE_PREFIX_2 "Registered speech recognition engine '%s'\n", engine->name); /* Add to the engine linked list and make default if needed */ AST_LIST_LOCK(&engines); AST_LIST_INSERT_HEAD(&engines, engine, list); if (default_engine == NULL) { default_engine = engine; if (option_verbose > 1) ast_verbose(VERBOSE_PREFIX_2 "Made '%s' the default speech recognition engine\n", engine->name); } AST_LIST_UNLOCK(&engines); return res; } /*! \brief Unregister a speech recognition engine */ int ast_speech_unregister(char *engine_name) { struct ast_speech_engine *engine = NULL; int res = -1; if (engine_name == NULL) { return res; } AST_LIST_LOCK(&engines); AST_LIST_TRAVERSE_SAFE_BEGIN(&engines, engine, list) { if (!strcasecmp(engine->name, engine_name)) { /* We have our engine... removed it */ AST_LIST_REMOVE_CURRENT(&engines, list); /* If this was the default engine, we need to pick a new one */ if (default_engine == engine) { default_engine = AST_LIST_FIRST(&engines); } if (option_verbose > 1) ast_verbose(VERBOSE_PREFIX_2 "Unregistered speech recognition engine '%s'\n", engine_name); /* All went well */ res = 0; break; } } AST_LIST_TRAVERSE_SAFE_END AST_LIST_UNLOCK(&engines); return res; } static int unload_module(void) { /* We can not be unloaded */ return -1; } static int load_module(void) { int res = 0; /* Initialize our list of engines */ AST_LIST_HEAD_INIT_NOLOCK(&engines); return res; } AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Generic Speech Recognition API", .load = load_module, .unload = unload_module, ); asterisk-1.4.21.2/res/res_convert.c0000644000175000017500000001346310650166171016326 0ustar maniacmaniac/* * Asterisk -- An open source telephony toolkit. * * Copyright (C) 2005, 2006, Digium, Inc. * * redice li * Russell Bryant * * 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 file format conversion CLI command using Asterisk formats and translators * * \author redice li * \author Russell Bryant * */ #include "asterisk.h" ASTERISK_FILE_VERSION(__FILE__, "$Revision: 76067 $") #include #include #include #include "asterisk/channel.h" #include "asterisk/logger.h" #include "asterisk/module.h" #include "asterisk/cli.h" #include "asterisk/file.h" /*! \brief Split the filename to basename and extension */ static int split_ext(char *filename, char **name, char **ext) { *name = *ext = filename; if ((*ext = strrchr(filename, '.'))) { **ext = '\0'; (*ext)++; } if (ast_strlen_zero(*name) || ast_strlen_zero(*ext)) return -1; return 0; } /*! \brief Convert a file from one format to another */ static int cli_audio_convert_deprecated(int fd, int argc, char *argv[]) { int ret = RESULT_FAILURE; struct ast_filestream *fs_in = NULL, *fs_out = NULL; struct ast_frame *f; struct timeval start; int cost; char *file_in = NULL, *file_out = NULL; char *name_in, *ext_in, *name_out, *ext_out; /* ugly, can be removed when CLI entries have ast_module pointers */ ast_module_ref(ast_module_info->self); if (argc != 3 || ast_strlen_zero(argv[1]) || ast_strlen_zero(argv[2])) { ret = RESULT_SHOWUSAGE; goto fail_out; } file_in = ast_strdupa(argv[1]); file_out = ast_strdupa(argv[2]); if (split_ext(file_in, &name_in, &ext_in)) { ast_cli(fd, "'%s' is an invalid filename!\n", argv[1]); goto fail_out; } if (!(fs_in = ast_readfile(name_in, ext_in, NULL, O_RDONLY, 0, 0))) { ast_cli(fd, "Unable to open input file: %s\n", argv[1]); goto fail_out; } if (split_ext(file_out, &name_out, &ext_out)) { ast_cli(fd, "'%s' is an invalid filename!\n", argv[2]); goto fail_out; } if (!(fs_out = ast_writefile(name_out, ext_out, NULL, O_CREAT|O_TRUNC|O_WRONLY, 0, 0644))) { ast_cli(fd, "Unable to open output file: %s\n", argv[2]); goto fail_out; } start = ast_tvnow(); while ((f = ast_readframe(fs_in))) { if (ast_writestream(fs_out, f)) { ast_cli(fd, "Failed to convert %s.%s to %s.%s!\n", name_in, ext_in, name_out, ext_out); goto fail_out; } } cost = ast_tvdiff_ms(ast_tvnow(), start); ast_cli(fd, "Converted %s.%s to %s.%s in %dms\n", name_in, ext_in, name_out, ext_out, cost); ret = RESULT_SUCCESS; fail_out: if (fs_out) { ast_closestream(fs_out); if (ret != RESULT_SUCCESS) ast_filedelete(name_out, ext_out); } if (fs_in) ast_closestream(fs_in); ast_module_unref(ast_module_info->self); return ret; } static int cli_audio_convert(int fd, int argc, char *argv[]) { int ret = RESULT_FAILURE; struct ast_filestream *fs_in = NULL, *fs_out = NULL; struct ast_frame *f; struct timeval start; int cost; char *file_in = NULL, *file_out = NULL; char *name_in, *ext_in, *name_out, *ext_out; /* ugly, can be removed when CLI entries have ast_module pointers */ ast_module_ref(ast_module_info->self); if (argc != 4 || ast_strlen_zero(argv[2]) || ast_strlen_zero(argv[3])) { ret = RESULT_SHOWUSAGE; goto fail_out; } file_in = ast_strdupa(argv[2]); file_out = ast_strdupa(argv[3]); if (split_ext(file_in, &name_in, &ext_in)) { ast_cli(fd, "'%s' is an invalid filename!\n", argv[2]); goto fail_out; } if (!(fs_in = ast_readfile(name_in, ext_in, NULL, O_RDONLY, 0, 0))) { ast_cli(fd, "Unable to open input file: %s\n", argv[2]); goto fail_out; } if (split_ext(file_out, &name_out, &ext_out)) { ast_cli(fd, "'%s' is an invalid filename!\n", argv[3]); goto fail_out; } if (!(fs_out = ast_writefile(name_out, ext_out, NULL, O_CREAT|O_TRUNC|O_WRONLY, 0, 0644))) { ast_cli(fd, "Unable to open output file: %s\n", argv[3]); goto fail_out; } start = ast_tvnow(); while ((f = ast_readframe(fs_in))) { if (ast_writestream(fs_out, f)) { ast_cli(fd, "Failed to convert %s.%s to %s.%s!\n", name_in, ext_in, name_out, ext_out); goto fail_out; } } cost = ast_tvdiff_ms(ast_tvnow(), start); ast_cli(fd, "Converted %s.%s to %s.%s in %dms\n", name_in, ext_in, name_out, ext_out, cost); ret = RESULT_SUCCESS; fail_out: if (fs_out) { ast_closestream(fs_out); if (ret != RESULT_SUCCESS) ast_filedelete(name_out, ext_out); } if (fs_in) ast_closestream(fs_in); ast_module_unref(ast_module_info->self); return ret; } static char usage_audio_convert[] = "Usage: file convert \n" " Convert from file_in to file_out. If an absolute path is not given, the\n" "default Asterisk sounds directory will be used.\n\n" "Example:\n" " file convert tt-weasels.gsm tt-weasels.ulaw\n"; static struct ast_cli_entry cli_convert_deprecated = { { "convert" , NULL }, cli_audio_convert_deprecated, NULL, NULL }; static struct ast_cli_entry cli_convert[] = { { { "file", "convert" , NULL }, cli_audio_convert, "Convert audio file", usage_audio_convert, NULL, &cli_convert_deprecated }, }; static int unload_module(void) { ast_cli_unregister_multiple(cli_convert, sizeof(cli_convert) / sizeof(struct ast_cli_entry)); return 0; } static int load_module(void) { ast_cli_register_multiple(cli_convert, sizeof(cli_convert) / sizeof(struct ast_cli_entry)); return 0; } AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "File format conversion CLI command"); asterisk-1.4.21.2/res/res_agi.c0000644000175000017500000020460111012655701015375 0ustar maniacmaniac/* * Asterisk -- An open source telephony toolkit. * * Copyright (C) 1999 - 2006, Digium, Inc. * * Mark Spencer * * 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 AGI - the Asterisk Gateway Interface * * \author Mark Spencer */ #include "asterisk.h" ASTERISK_FILE_VERSION(__FILE__, "$Revision: 116466 $") #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "asterisk/file.h" #include "asterisk/logger.h" #include "asterisk/channel.h" #include "asterisk/pbx.h" #include "asterisk/module.h" #include "asterisk/astdb.h" #include "asterisk/callerid.h" #include "asterisk/cli.h" #include "asterisk/logger.h" #include "asterisk/options.h" #include "asterisk/image.h" #include "asterisk/say.h" #include "asterisk/app.h" #include "asterisk/dsp.h" #include "asterisk/musiconhold.h" #include "asterisk/utils.h" #include "asterisk/lock.h" #include "asterisk/strings.h" #include "asterisk/agi.h" #define MAX_ARGS 128 #define MAX_COMMANDS 128 #define AGI_NANDFS_RETRY 3 #define AGI_BUF_LEN 2048 /* Recycle some stuff from the CLI interface */ #define fdprintf agi_debug_cli static char *app = "AGI"; static char *eapp = "EAGI"; static char *deadapp = "DeadAGI"; static char *synopsis = "Executes an AGI compliant application"; static char *esynopsis = "Executes an EAGI compliant application"; static char *deadsynopsis = "Executes AGI on a hungup channel"; static char *descrip = " [E|Dead]AGI(command|args): Executes an Asterisk Gateway Interface compliant\n" "program on a channel. AGI allows Asterisk to launch external programs\n" "written in any language to control a telephony channel, play audio,\n" "read DTMF digits, etc. by communicating with the AGI protocol on stdin\n" "and stdout.\n" " This channel will stop dialplan execution on hangup inside of this\n" "application, except when using DeadAGI. Otherwise, dialplan execution\n" "will continue normally.\n" " A locally executed AGI script will receive SIGHUP on hangup from the channel\n" "except when using DeadAGI. This can be disabled by setting the AGISIGHUP channel\n" "variable to \"no\" before executing the AGI application.\n" " Using 'EAGI' provides enhanced AGI, with incoming audio available out of band\n" "on file descriptor 3\n\n" " Use the CLI command 'agi show' to list available agi commands\n" " This application sets the following channel variable upon completion:\n" " AGISTATUS The status of the attempt to the run the AGI script\n" " text string, one of SUCCESS | FAILURE | HANGUP\n"; static int agidebug = 0; #define TONE_BLOCK_SIZE 200 /* Max time to connect to an AGI remote host */ #define MAX_AGI_CONNECT 2000 #define AGI_PORT 4573 enum agi_result { AGI_RESULT_SUCCESS, AGI_RESULT_SUCCESS_FAST, AGI_RESULT_FAILURE, AGI_RESULT_HANGUP }; static int agi_debug_cli(int fd, char *fmt, ...) { char *stuff; int res = 0; va_list ap; va_start(ap, fmt); res = vasprintf(&stuff, fmt, ap); va_end(ap); if (res == -1) { ast_log(LOG_ERROR, "Out of memory\n"); } else { if (agidebug) ast_verbose("AGI Tx >> %s", stuff); /* \n provided by caller */ res = ast_carefulwrite(fd, stuff, strlen(stuff), 100); free(stuff); } return res; } /* launch_netscript: The fastagi handler. FastAGI defaults to port 4573 */ static enum agi_result launch_netscript(char *agiurl, char *argv[], int *fds, int *efd, int *opid) { int s; int flags; struct pollfd pfds[1]; char *host; char *c; int port = AGI_PORT; char *script=""; struct sockaddr_in sin; struct hostent *hp; struct ast_hostent ahp; int res; /* agiusl is "agi://host.domain[:port][/script/name]" */ host = ast_strdupa(agiurl + 6); /* Remove agi:// */ /* Strip off any script name */ if ((c = strchr(host, '/'))) { *c = '\0'; c++; script = c; } if ((c = strchr(host, ':'))) { *c = '\0'; c++; port = atoi(c); } if (efd) { ast_log(LOG_WARNING, "AGI URI's don't support Enhanced AGI yet\n"); return -1; } hp = ast_gethostbyname(host, &ahp); if (!hp) { ast_log(LOG_WARNING, "Unable to locate host '%s'\n", host); return -1; } s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) { ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno)); return -1; } flags = fcntl(s, F_GETFL); if (flags < 0) { ast_log(LOG_WARNING, "Fcntl(F_GETFL) failed: %s\n", strerror(errno)); close(s); return -1; } if (fcntl(s, F_SETFL, flags | O_NONBLOCK) < 0) { ast_log(LOG_WARNING, "Fnctl(F_SETFL) failed: %s\n", strerror(errno)); close(s); return -1; } memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(port); memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr)); if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) && (errno != EINPROGRESS)) { ast_log(LOG_WARNING, "Connect failed with unexpected error: %s\n", strerror(errno)); close(s); return AGI_RESULT_FAILURE; } pfds[0].fd = s; pfds[0].events = POLLOUT; while ((res = poll(pfds, 1, MAX_AGI_CONNECT)) != 1) { if (errno != EINTR) { if (!res) { ast_log(LOG_WARNING, "FastAGI connection to '%s' timed out after MAX_AGI_CONNECT (%d) milliseconds.\n", agiurl, MAX_AGI_CONNECT); } else ast_log(LOG_WARNING, "Connect to '%s' failed: %s\n", agiurl, strerror(errno)); close(s); return AGI_RESULT_FAILURE; } } if (fdprintf(s, "agi_network: yes\n") < 0) { if (errno != EINTR) { ast_log(LOG_WARNING, "Connect to '%s' failed: %s\n", agiurl, strerror(errno)); close(s); return AGI_RESULT_FAILURE; } } /* If we have a script parameter, relay it to the fastagi server */ if (!ast_strlen_zero(script)) fdprintf(s, "agi_network_script: %s\n", script); if (option_debug > 3) ast_log(LOG_DEBUG, "Wow, connected!\n"); fds[0] = s; fds[1] = s; *opid = -1; return AGI_RESULT_SUCCESS_FAST; } static enum agi_result launch_script(char *script, char *argv[], int *fds, int *efd, int *opid) { char tmp[256]; int pid; int toast[2]; int fromast[2]; int audio[2]; int x; int res; sigset_t signal_set, old_set; if (!strncasecmp(script, "agi://", 6)) return launch_netscript(script, argv, fds, efd, opid); if (script[0] != '/') { snprintf(tmp, sizeof(tmp), "%s/%s", (char *)ast_config_AST_AGI_DIR, script); script = tmp; } if (pipe(toast)) { ast_log(LOG_WARNING, "Unable to create toast pipe: %s\n",strerror(errno)); return AGI_RESULT_FAILURE; } if (pipe(fromast)) { ast_log(LOG_WARNING, "unable to create fromast pipe: %s\n", strerror(errno)); close(toast[0]); close(toast[1]); return AGI_RESULT_FAILURE; } if (efd) { if (pipe(audio)) { ast_log(LOG_WARNING, "unable to create audio pipe: %s\n", strerror(errno)); close(fromast[0]); close(fromast[1]); close(toast[0]); close(toast[1]); return AGI_RESULT_FAILURE; } res = fcntl(audio[1], F_GETFL); if (res > -1) res = fcntl(audio[1], F_SETFL, res | O_NONBLOCK); if (res < 0) { ast_log(LOG_WARNING, "unable to set audio pipe parameters: %s\n", strerror(errno)); close(fromast[0]); close(fromast[1]); close(toast[0]); close(toast[1]); close(audio[0]); close(audio[1]); return AGI_RESULT_FAILURE; } } /* Block SIGHUP during the fork - prevents a race */ sigfillset(&signal_set); pthread_sigmask(SIG_BLOCK, &signal_set, &old_set); pid = fork(); if (pid < 0) { ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno)); pthread_sigmask(SIG_SETMASK, &old_set, NULL); return AGI_RESULT_FAILURE; } if (!pid) { /* Pass paths to AGI via environmental variables */ setenv("AST_CONFIG_DIR", ast_config_AST_CONFIG_DIR, 1); setenv("AST_CONFIG_FILE", ast_config_AST_CONFIG_FILE, 1); setenv("AST_MODULE_DIR", ast_config_AST_MODULE_DIR, 1); setenv("AST_SPOOL_DIR", ast_config_AST_SPOOL_DIR, 1); setenv("AST_MONITOR_DIR", ast_config_AST_MONITOR_DIR, 1); setenv("AST_VAR_DIR", ast_config_AST_VAR_DIR, 1); setenv("AST_DATA_DIR", ast_config_AST_DATA_DIR, 1); setenv("AST_LOG_DIR", ast_config_AST_LOG_DIR, 1); setenv("AST_AGI_DIR", ast_config_AST_AGI_DIR, 1); setenv("AST_KEY_DIR", ast_config_AST_KEY_DIR, 1); setenv("AST_RUN_DIR", ast_config_AST_RUN_DIR, 1); /* Don't run AGI scripts with realtime priority -- it causes audio stutter */ ast_set_priority(0); /* Redirect stdin and out, provide enhanced audio channel if desired */ dup2(fromast[0], STDIN_FILENO); dup2(toast[1], STDOUT_FILENO); if (efd) { dup2(audio[0], STDERR_FILENO + 1); } else { close(STDERR_FILENO + 1); } /* Before we unblock our signals, return our trapped signals back to the defaults */ signal(SIGHUP, SIG_DFL); signal(SIGCHLD, SIG_DFL); signal(SIGINT, SIG_DFL); signal(SIGURG, SIG_DFL); signal(SIGTERM, SIG_DFL); signal(SIGPIPE, SIG_DFL); signal(SIGXFSZ, SIG_DFL); /* unblock important signal handlers */ if (pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL)) { ast_log(LOG_WARNING, "unable to unblock signals for AGI script: %s\n", strerror(errno)); _exit(1); } /* Close everything but stdin/out/error */ for (x=STDERR_FILENO + 2;x<1024;x++) close(x); /* Execute script */ execv(script, argv); /* Can't use ast_log since FD's are closed */ fprintf(stdout, "verbose \"Failed to execute '%s': %s\" 2\n", script, strerror(errno)); /* Special case to set status of AGI to failure */ fprintf(stdout, "failure\n"); fflush(stdout); _exit(1); } pthread_sigmask(SIG_SETMASK, &old_set, NULL); if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Launched AGI Script %s\n", script); fds[0] = toast[0]; fds[1] = fromast[1]; if (efd) { *efd = audio[1]; } /* close what we're not using in the parent */ close(toast[1]); close(fromast[0]); if (efd) close(audio[0]); *opid = pid; return AGI_RESULT_SUCCESS; } static void setup_env(struct ast_channel *chan, char *request, int fd, int enhanced) { /* Print initial environment, with agi_request always being the first thing */ fdprintf(fd, "agi_request: %s\n", request); fdprintf(fd, "agi_channel: %s\n", chan->name); fdprintf(fd, "agi_language: %s\n", chan->language); fdprintf(fd, "agi_type: %s\n", chan->tech->type); fdprintf(fd, "agi_uniqueid: %s\n", chan->uniqueid); /* ANI/DNIS */ fdprintf(fd, "agi_callerid: %s\n", S_OR(chan->cid.cid_num, "unknown")); fdprintf(fd, "agi_calleridname: %s\n", S_OR(chan->cid.cid_name, "unknown")); fdprintf(fd, "agi_callingpres: %d\n", chan->cid.cid_pres); fdprintf(fd, "agi_callingani2: %d\n", chan->cid.cid_ani2); fdprintf(fd, "agi_callington: %d\n", chan->cid.cid_ton); fdprintf(fd, "agi_callingtns: %d\n", chan->cid.cid_tns); fdprintf(fd, "agi_dnid: %s\n", S_OR(chan->cid.cid_dnid, "unknown")); fdprintf(fd, "agi_rdnis: %s\n", S_OR(chan->cid.cid_rdnis, "unknown")); /* Context information */ fdprintf(fd, "agi_context: %s\n", chan->context); fdprintf(fd, "agi_extension: %s\n", chan->exten); fdprintf(fd, "agi_priority: %d\n", chan->priority); fdprintf(fd, "agi_enhanced: %s\n", enhanced ? "1.0" : "0.0"); /* User information */ fdprintf(fd, "agi_accountcode: %s\n", chan->accountcode ? chan->accountcode : ""); /* End with empty return */ fdprintf(fd, "\n"); } static int handle_answer(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int res; res = 0; if (chan->_state != AST_STATE_UP) { /* Answer the chan */ res = ast_answer(chan); } fdprintf(agi->fd, "200 result=%d\n", res); return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE; } static int handle_waitfordigit(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int res; int to; if (argc != 4) return RESULT_SHOWUSAGE; if (sscanf(argv[3], "%d", &to) != 1) return RESULT_SHOWUSAGE; res = ast_waitfordigit_full(chan, to, agi->audio, agi->ctrl); fdprintf(agi->fd, "200 result=%d\n", res); return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE; } static int handle_sendtext(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int res; if (argc != 3) return RESULT_SHOWUSAGE; /* At the moment, the parser (perhaps broken) returns with the last argument PLUS the newline at the end of the input buffer. This probably needs to be fixed, but I wont do that because other stuff may break as a result. The right way would probably be to strip off the trailing newline before parsing, then here, add a newline at the end of the string before sending it to ast_sendtext --DUDE */ res = ast_sendtext(chan, argv[2]); fdprintf(agi->fd, "200 result=%d\n", res); return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE; } static int handle_recvchar(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int res; if (argc != 3) return RESULT_SHOWUSAGE; res = ast_recvchar(chan,atoi(argv[2])); if (res == 0) { fdprintf(agi->fd, "200 result=%d (timeout)\n", res); return RESULT_SUCCESS; } if (res > 0) { fdprintf(agi->fd, "200 result=%d\n", res); return RESULT_SUCCESS; } else { fdprintf(agi->fd, "200 result=%d (hangup)\n", res); return RESULT_FAILURE; } } static int handle_recvtext(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { char *buf; if (argc != 3) return RESULT_SHOWUSAGE; buf = ast_recvtext(chan,atoi(argv[2])); if (buf) { fdprintf(agi->fd, "200 result=1 (%s)\n", buf); free(buf); } else { fdprintf(agi->fd, "200 result=-1\n"); } return RESULT_SUCCESS; } static int handle_tddmode(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int res,x; if (argc != 3) return RESULT_SHOWUSAGE; if (!strncasecmp(argv[2],"on",2)) x = 1; else x = 0; if (!strncasecmp(argv[2],"mate",4)) x = 2; if (!strncasecmp(argv[2],"tdd",3)) x = 1; res = ast_channel_setoption(chan, AST_OPTION_TDD, &x, sizeof(char), 0); if (res != RESULT_SUCCESS) fdprintf(agi->fd, "200 result=0\n"); else fdprintf(agi->fd, "200 result=1\n"); return RESULT_SUCCESS; } static int handle_sendimage(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int res; if (argc != 3) return RESULT_SHOWUSAGE; res = ast_send_image(chan, argv[2]); if (!ast_check_hangup(chan)) res = 0; fdprintf(agi->fd, "200 result=%d\n", res); return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE; } static int handle_controlstreamfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int res = 0; int skipms = 3000; char *fwd = NULL; char *rev = NULL; char *pause = NULL; char *stop = NULL; if (argc < 5 || argc > 9) return RESULT_SHOWUSAGE; if (!ast_strlen_zero(argv[4])) stop = argv[4]; else stop = NULL; if ((argc > 5) && (sscanf(argv[5], "%d", &skipms) != 1)) return RESULT_SHOWUSAGE; if (argc > 6 && !ast_strlen_zero(argv[6])) fwd = argv[6]; else fwd = "#"; if (argc > 7 && !ast_strlen_zero(argv[7])) rev = argv[7]; else rev = "*"; if (argc > 8 && !ast_strlen_zero(argv[8])) pause = argv[8]; else pause = NULL; res = ast_control_streamfile(chan, argv[3], fwd, rev, stop, pause, NULL, skipms); fdprintf(agi->fd, "200 result=%d\n", res); return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE; } static int handle_streamfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int res; int vres; struct ast_filestream *fs; struct ast_filestream *vfs; long sample_offset = 0; long max_length; char *edigits = ""; if (argc < 4 || argc > 5) return RESULT_SHOWUSAGE; if (argv[3]) edigits = argv[3]; if ((argc > 4) && (sscanf(argv[4], "%ld", &sample_offset) != 1)) return RESULT_SHOWUSAGE; fs = ast_openstream(chan, argv[2], chan->language); if (!fs) { fdprintf(agi->fd, "200 result=%d endpos=%ld\n", 0, sample_offset); return RESULT_SUCCESS; } vfs = ast_openvstream(chan, argv[2], chan->language); if (vfs) ast_log(LOG_DEBUG, "Ooh, found a video stream, too\n"); if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Playing '%s' (escape_digits=%s) (sample_offset %ld)\n", argv[2], edigits, sample_offset); ast_seekstream(fs, 0, SEEK_END); max_length = ast_tellstream(fs); ast_seekstream(fs, sample_offset, SEEK_SET); res = ast_applystream(chan, fs); if (vfs) vres = ast_applystream(chan, vfs); ast_playstream(fs); if (vfs) ast_playstream(vfs); res = ast_waitstream_full(chan, argv[3], agi->audio, agi->ctrl); /* this is to check for if ast_waitstream closed the stream, we probably are at * the end of the stream, return that amount, else check for the amount */ sample_offset = (chan->stream) ? ast_tellstream(fs) : max_length; ast_stopstream(chan); if (res == 1) { /* Stop this command, don't print a result line, as there is a new command */ return RESULT_SUCCESS; } fdprintf(agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset); return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE; } /* get option - really similar to the handle_streamfile, but with a timeout */ static int handle_getoption(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int res; int vres; struct ast_filestream *fs; struct ast_filestream *vfs; long sample_offset = 0; long max_length; int timeout = 0; char *edigits = ""; if ( argc < 4 || argc > 5 ) return RESULT_SHOWUSAGE; if ( argv[3] ) edigits = argv[3]; if ( argc == 5 ) timeout = atoi(argv[4]); else if (chan->pbx->dtimeout) { /* by default dtimeout is set to 5sec */ timeout = chan->pbx->dtimeout * 1000; /* in msec */ } fs = ast_openstream(chan, argv[2], chan->language); if (!fs) { fdprintf(agi->fd, "200 result=%d endpos=%ld\n", 0, sample_offset); ast_log(LOG_WARNING, "Unable to open %s\n", argv[2]); return RESULT_SUCCESS; } vfs = ast_openvstream(chan, argv[2], chan->language); if (vfs) ast_log(LOG_DEBUG, "Ooh, found a video stream, too\n"); if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Playing '%s' (escape_digits=%s) (timeout %d)\n", argv[2], edigits, timeout); ast_seekstream(fs, 0, SEEK_END); max_length = ast_tellstream(fs); ast_seekstream(fs, sample_offset, SEEK_SET); res = ast_applystream(chan, fs); if (vfs) vres = ast_applystream(chan, vfs); ast_playstream(fs); if (vfs) ast_playstream(vfs); res = ast_waitstream_full(chan, argv[3], agi->audio, agi->ctrl); /* this is to check for if ast_waitstream closed the stream, we probably are at * the end of the stream, return that amount, else check for the amount */ sample_offset = (chan->stream)?ast_tellstream(fs):max_length; ast_stopstream(chan); if (res == 1) { /* Stop this command, don't print a result line, as there is a new command */ return RESULT_SUCCESS; } /* If the user didnt press a key, wait for digitTimeout*/ if (res == 0 ) { res = ast_waitfordigit_full(chan, timeout, agi->audio, agi->ctrl); /* Make sure the new result is in the escape digits of the GET OPTION */ if ( !strchr(edigits,res) ) res=0; } fdprintf(agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset); return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE; } /*--- handle_saynumber: Say number in various language syntaxes ---*/ /* Need to add option for gender here as well. Coders wanted */ /* While waiting, we're sending a (char *) NULL. */ static int handle_saynumber(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int res; int num; if (argc != 4) return RESULT_SHOWUSAGE; if (sscanf(argv[2], "%d", &num) != 1) return RESULT_SHOWUSAGE; res = ast_say_number_full(chan, num, argv[3], chan->language, (char *) NULL, agi->audio, agi->ctrl); if (res == 1) return RESULT_SUCCESS; fdprintf(agi->fd, "200 result=%d\n", res); return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE; } static int handle_saydigits(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int res; int num; if (argc != 4) return RESULT_SHOWUSAGE; if (sscanf(argv[2], "%d", &num) != 1) return RESULT_SHOWUSAGE; res = ast_say_digit_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl); if (res == 1) /* New command */ return RESULT_SUCCESS; fdprintf(agi->fd, "200 result=%d\n", res); return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE; } static int handle_sayalpha(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int res; if (argc != 4) return RESULT_SHOWUSAGE; res = ast_say_character_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl); if (res == 1) /* New command */ return RESULT_SUCCESS; fdprintf(agi->fd, "200 result=%d\n", res); return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE; } static int handle_saydate(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int res; int num; if (argc != 4) return RESULT_SHOWUSAGE; if (sscanf(argv[2], "%d", &num) != 1) return RESULT_SHOWUSAGE; res = ast_say_date(chan, num, argv[3], chan->language); if (res == 1) return RESULT_SUCCESS; fdprintf(agi->fd, "200 result=%d\n", res); return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE; } static int handle_saytime(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int res; int num; if (argc != 4) return RESULT_SHOWUSAGE; if (sscanf(argv[2], "%d", &num) != 1) return RESULT_SHOWUSAGE; res = ast_say_time(chan, num, argv[3], chan->language); if (res == 1) return RESULT_SUCCESS; fdprintf(agi->fd, "200 result=%d\n", res); return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE; } static int handle_saydatetime(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int res=0; time_t unixtime; char *format, *zone=NULL; if (argc < 4) return RESULT_SHOWUSAGE; if (argc > 4) { format = argv[4]; } else { /* XXX this doesn't belong here, but in the 'say' module */ if (!strcasecmp(chan->language, "de")) { format = "A dBY HMS"; } else { format = "ABdY 'digits/at' IMp"; } } if (argc > 5 && !ast_strlen_zero(argv[5])) zone = argv[5]; if (ast_get_time_t(argv[2], &unixtime, 0, NULL)) return RESULT_SHOWUSAGE; res = ast_say_date_with_format(chan, unixtime, argv[3], chan->language, format, zone); if (res == 1) return RESULT_SUCCESS; fdprintf(agi->fd, "200 result=%d\n", res); return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE; } static int handle_sayphonetic(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int res; if (argc != 4) return RESULT_SHOWUSAGE; res = ast_say_phonetic_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl); if (res == 1) /* New command */ return RESULT_SUCCESS; fdprintf(agi->fd, "200 result=%d\n", res); return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE; } static int handle_getdata(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int res; char data[1024]; int max; int timeout; if (argc < 3) return RESULT_SHOWUSAGE; if (argc >= 4) timeout = atoi(argv[3]); else timeout = 0; if (argc >= 5) max = atoi(argv[4]); else max = 1024; res = ast_app_getdata_full(chan, argv[2], data, max, timeout, agi->audio, agi->ctrl); if (res == 2) /* New command */ return RESULT_SUCCESS; else if (res == 1) fdprintf(agi->fd, "200 result=%s (timeout)\n", data); else if (res < 0 ) fdprintf(agi->fd, "200 result=-1\n"); else fdprintf(agi->fd, "200 result=%s\n", data); return RESULT_SUCCESS; } static int handle_setcontext(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { if (argc != 3) return RESULT_SHOWUSAGE; ast_copy_string(chan->context, argv[2], sizeof(chan->context)); fdprintf(agi->fd, "200 result=0\n"); return RESULT_SUCCESS; } static int handle_setextension(struct ast_channel *chan, AGI *agi, int argc, char **argv) { if (argc != 3) return RESULT_SHOWUSAGE; ast_copy_string(chan->exten, argv[2], sizeof(chan->exten)); fdprintf(agi->fd, "200 result=0\n"); return RESULT_SUCCESS; } static int handle_setpriority(struct ast_channel *chan, AGI *agi, int argc, char **argv) { int pri; if (argc != 3) return RESULT_SHOWUSAGE; if (sscanf(argv[2], "%d", &pri) != 1) { if ((pri = ast_findlabel_extension(chan, chan->context, chan->exten, argv[2], chan->cid.cid_num)) < 1) return RESULT_SHOWUSAGE; } ast_explicit_goto(chan, NULL, NULL, pri); fdprintf(agi->fd, "200 result=0\n"); return RESULT_SUCCESS; } static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { struct ast_filestream *fs; struct ast_frame *f; struct timeval start; long sample_offset = 0; int res = 0; int ms; struct ast_dsp *sildet=NULL; /* silence detector dsp */ int totalsilence = 0; int dspsilence = 0; int silence = 0; /* amount of silence to allow */ int gotsilence = 0; /* did we timeout for silence? */ char *silencestr=NULL; int rfmt=0; /* XXX EAGI FIXME XXX */ if (argc < 6) return RESULT_SHOWUSAGE; if (sscanf(argv[5], "%d", &ms) != 1) return RESULT_SHOWUSAGE; if (argc > 6) silencestr = strchr(argv[6],'s'); if ((argc > 7) && (!silencestr)) silencestr = strchr(argv[7],'s'); if ((argc > 8) && (!silencestr)) silencestr = strchr(argv[8],'s'); if (silencestr) { if (strlen(silencestr) > 2) { if ((silencestr[0] == 's') && (silencestr[1] == '=')) { silencestr++; silencestr++; if (silencestr) silence = atoi(silencestr); if (silence > 0) silence *= 1000; } } } if (silence > 0) { rfmt = chan->readformat; res = ast_set_read_format(chan, AST_FORMAT_SLINEAR); if (res < 0) { ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n"); return -1; } sildet = ast_dsp_new(); if (!sildet) { ast_log(LOG_WARNING, "Unable to create silence detector :(\n"); return -1; } ast_dsp_set_threshold(sildet, 256); } /* backward compatibility, if no offset given, arg[6] would have been * caught below and taken to be a beep, else if it is a digit then it is a * offset */ if ((argc >6) && (sscanf(argv[6], "%ld", &sample_offset) != 1) && (!strchr(argv[6], '='))) res = ast_streamfile(chan, "beep", chan->language); if ((argc > 7) && (!strchr(argv[7], '='))) res = ast_streamfile(chan, "beep", chan->language); if (!res) res = ast_waitstream(chan, argv[4]); if (res) { fdprintf(agi->fd, "200 result=%d (randomerror) endpos=%ld\n", res, sample_offset); } else { fs = ast_writefile(argv[2], argv[3], NULL, O_CREAT | O_WRONLY | (sample_offset ? O_APPEND : 0), 0, 0644); if (!fs) { res = -1; fdprintf(agi->fd, "200 result=%d (writefile)\n", res); if (sildet) ast_dsp_free(sildet); return RESULT_FAILURE; } /* Request a video update */ ast_indicate(chan, AST_CONTROL_VIDUPDATE); chan->stream = fs; ast_applystream(chan,fs); /* really should have checks */ ast_seekstream(fs, sample_offset, SEEK_SET); ast_truncstream(fs); start = ast_tvnow(); while ((ms < 0) || ast_tvdiff_ms(ast_tvnow(), start) < ms) { res = ast_waitfor(chan, -1); if (res < 0) { ast_closestream(fs); fdprintf(agi->fd, "200 result=%d (waitfor) endpos=%ld\n", res,sample_offset); if (sildet) ast_dsp_free(sildet); return RESULT_FAILURE; } f = ast_read(chan); if (!f) { fdprintf(agi->fd, "200 result=%d (hangup) endpos=%ld\n", -1, sample_offset); ast_closestream(fs); if (sildet) ast_dsp_free(sildet); return RESULT_FAILURE; } switch(f->frametype) { case AST_FRAME_DTMF: if (strchr(argv[4], f->subclass)) { /* This is an interrupting chracter, so rewind to chop off any small amount of DTMF that may have been recorded */ ast_stream_rewind(fs, 200); ast_truncstream(fs); sample_offset = ast_tellstream(fs); fdprintf(agi->fd, "200 result=%d (dtmf) endpos=%ld\n", f->subclass, sample_offset); ast_closestream(fs); ast_frfree(f); if (sildet) ast_dsp_free(sildet); return RESULT_SUCCESS; } break; case AST_FRAME_VOICE: ast_writestream(fs, f); /* this is a safe place to check progress since we know that fs * is valid after a write, and it will then have our current * location */ sample_offset = ast_tellstream(fs); if (silence > 0) { dspsilence = 0; ast_dsp_silence(sildet, f, &dspsilence); if (dspsilence) { totalsilence = dspsilence; } else { totalsilence = 0; } if (totalsilence > silence) { /* Ended happily with silence */ gotsilence = 1; break; } } break; case AST_FRAME_VIDEO: ast_writestream(fs, f); default: /* Ignore all other frames */ break; } ast_frfree(f); if (gotsilence) break; } if (gotsilence) { ast_stream_rewind(fs, silence-1000); ast_truncstream(fs); sample_offset = ast_tellstream(fs); } fdprintf(agi->fd, "200 result=%d (timeout) endpos=%ld\n", res, sample_offset); ast_closestream(fs); } if (silence > 0) { res = ast_set_read_format(chan, rfmt); if (res) ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name); ast_dsp_free(sildet); } return RESULT_SUCCESS; } static int handle_autohangup(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int timeout; if (argc != 3) return RESULT_SHOWUSAGE; if (sscanf(argv[2], "%d", &timeout) != 1) return RESULT_SHOWUSAGE; if (timeout < 0) timeout = 0; if (timeout) chan->whentohangup = time(NULL) + timeout; else chan->whentohangup = 0; fdprintf(agi->fd, "200 result=0\n"); return RESULT_SUCCESS; } static int handle_hangup(struct ast_channel *chan, AGI *agi, int argc, char **argv) { struct ast_channel *c; if (argc == 1) { /* no argument: hangup the current channel */ ast_softhangup(chan,AST_SOFTHANGUP_EXPLICIT); fdprintf(agi->fd, "200 result=1\n"); return RESULT_SUCCESS; } else if (argc == 2) { /* one argument: look for info on the specified channel */ c = ast_get_channel_by_name_locked(argv[1]); if (c) { /* we have a matching channel */ ast_softhangup(c,AST_SOFTHANGUP_EXPLICIT); fdprintf(agi->fd, "200 result=1\n"); ast_channel_unlock(c); return RESULT_SUCCESS; } /* if we get this far no channel name matched the argument given */ fdprintf(agi->fd, "200 result=-1\n"); return RESULT_SUCCESS; } else { return RESULT_SHOWUSAGE; } } static int handle_exec(struct ast_channel *chan, AGI *agi, int argc, char **argv) { int res; struct ast_app *app; if (argc < 2) return RESULT_SHOWUSAGE; if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "AGI Script Executing Application: (%s) Options: (%s)\n", argv[1], argv[2]); app = pbx_findapp(argv[1]); if (app) { res = pbx_exec(chan, app, argv[2]); } else { ast_log(LOG_WARNING, "Could not find application (%s)\n", argv[1]); res = -2; } fdprintf(agi->fd, "200 result=%d\n", res); /* Even though this is wrong, users are depending upon this result. */ return res; } static int handle_setcallerid(struct ast_channel *chan, AGI *agi, int argc, char **argv) { char tmp[256]=""; char *l = NULL, *n = NULL; if (argv[2]) { ast_copy_string(tmp, argv[2], sizeof(tmp)); ast_callerid_parse(tmp, &n, &l); if (l) ast_shrink_phone_number(l); else l = ""; if (!n) n = ""; ast_set_callerid(chan, l, n, NULL); } fdprintf(agi->fd, "200 result=1\n"); return RESULT_SUCCESS; } static int handle_channelstatus(struct ast_channel *chan, AGI *agi, int argc, char **argv) { struct ast_channel *c; if (argc == 2) { /* no argument: supply info on the current channel */ fdprintf(agi->fd, "200 result=%d\n", chan->_state); return RESULT_SUCCESS; } else if (argc == 3) { /* one argument: look for info on the specified channel */ c = ast_get_channel_by_name_locked(argv[2]); if (c) { fdprintf(agi->fd, "200 result=%d\n", c->_state); ast_channel_unlock(c); return RESULT_SUCCESS; } /* if we get this far no channel name matched the argument given */ fdprintf(agi->fd, "200 result=-1\n"); return RESULT_SUCCESS; } else { return RESULT_SHOWUSAGE; } } static int handle_setvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv) { if (argv[3]) pbx_builtin_setvar_helper(chan, argv[2], argv[3]); fdprintf(agi->fd, "200 result=1\n"); return RESULT_SUCCESS; } static int handle_getvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv) { char *ret; char tempstr[1024]; if (argc != 3) return RESULT_SHOWUSAGE; /* check if we want to execute an ast_custom_function */ if (!ast_strlen_zero(argv[2]) && (argv[2][strlen(argv[2]) - 1] == ')')) { ret = ast_func_read(chan, argv[2], tempstr, sizeof(tempstr)) ? NULL : tempstr; } else { pbx_retrieve_variable(chan, argv[2], &ret, tempstr, sizeof(tempstr), NULL); } if (ret) fdprintf(agi->fd, "200 result=1 (%s)\n", ret); else fdprintf(agi->fd, "200 result=0\n"); return RESULT_SUCCESS; } static int handle_getvariablefull(struct ast_channel *chan, AGI *agi, int argc, char **argv) { char tmp[4096] = ""; struct ast_channel *chan2=NULL; if ((argc != 4) && (argc != 5)) return RESULT_SHOWUSAGE; if (argc == 5) { chan2 = ast_get_channel_by_name_locked(argv[4]); } else { chan2 = chan; } if (chan2) { pbx_substitute_variables_helper(chan2, argv[3], tmp, sizeof(tmp) - 1); fdprintf(agi->fd, "200 result=1 (%s)\n", tmp); } else { fdprintf(agi->fd, "200 result=0\n"); } if (chan2 && (chan2 != chan)) ast_channel_unlock(chan2); return RESULT_SUCCESS; } static int handle_verbose(struct ast_channel *chan, AGI *agi, int argc, char **argv) { int level = 0; char *prefix; if (argc < 2) return RESULT_SHOWUSAGE; if (argv[2]) sscanf(argv[2], "%d", &level); switch (level) { case 4: prefix = VERBOSE_PREFIX_4; break; case 3: prefix = VERBOSE_PREFIX_3; break; case 2: prefix = VERBOSE_PREFIX_2; break; case 1: default: prefix = VERBOSE_PREFIX_1; break; } if (level <= option_verbose) ast_verbose("%s %s: %s\n", prefix, chan->data, argv[1]); fdprintf(agi->fd, "200 result=1\n"); return RESULT_SUCCESS; } static int handle_dbget(struct ast_channel *chan, AGI *agi, int argc, char **argv) { int res; char tmp[256]; if (argc != 4) return RESULT_SHOWUSAGE; res = ast_db_get(argv[2], argv[3], tmp, sizeof(tmp)); if (res) fdprintf(agi->fd, "200 result=0\n"); else fdprintf(agi->fd, "200 result=1 (%s)\n", tmp); return RESULT_SUCCESS; } static int handle_dbput(struct ast_channel *chan, AGI *agi, int argc, char **argv) { int res; if (argc != 5) return RESULT_SHOWUSAGE; res = ast_db_put(argv[2], argv[3], argv[4]); fdprintf(agi->fd, "200 result=%c\n", res ? '0' : '1'); return RESULT_SUCCESS; } static int handle_dbdel(struct ast_channel *chan, AGI *agi, int argc, char **argv) { int res; if (argc != 4) return RESULT_SHOWUSAGE; res = ast_db_del(argv[2], argv[3]); fdprintf(agi->fd, "200 result=%c\n", res ? '0' : '1'); return RESULT_SUCCESS; } static int handle_dbdeltree(struct ast_channel *chan, AGI *agi, int argc, char **argv) { int res; if ((argc < 3) || (argc > 4)) return RESULT_SHOWUSAGE; if (argc == 4) res = ast_db_deltree(argv[2], argv[3]); else res = ast_db_deltree(argv[2], NULL); fdprintf(agi->fd, "200 result=%c\n", res ? '0' : '1'); return RESULT_SUCCESS; } static char debug_usage[] = "Usage: agi debug\n" " Enables dumping of AGI transactions for debugging purposes\n"; static char no_debug_usage[] = "Usage: agi debug off\n" " Disables dumping of AGI transactions for debugging purposes\n"; static int agi_do_debug(int fd, int argc, char *argv[]) { if (argc != 2) return RESULT_SHOWUSAGE; agidebug = 1; ast_cli(fd, "AGI Debugging Enabled\n"); return RESULT_SUCCESS; } static int agi_no_debug_deprecated(int fd, int argc, char *argv[]) { if (argc != 3) return RESULT_SHOWUSAGE; agidebug = 0; ast_cli(fd, "AGI Debugging Disabled\n"); return RESULT_SUCCESS; } static int agi_no_debug(int fd, int argc, char *argv[]) { if (argc != 3) return RESULT_SHOWUSAGE; agidebug = 0; ast_cli(fd, "AGI Debugging Disabled\n"); return RESULT_SUCCESS; } static int handle_noop(struct ast_channel *chan, AGI *agi, int arg, char *argv[]) { fdprintf(agi->fd, "200 result=0\n"); return RESULT_SUCCESS; } static int handle_setmusic(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { if (!strncasecmp(argv[2], "on", 2)) ast_moh_start(chan, argc > 3 ? argv[3] : NULL, NULL); else if (!strncasecmp(argv[2], "off", 3)) ast_moh_stop(chan); fdprintf(agi->fd, "200 result=0\n"); return RESULT_SUCCESS; } static char usage_setmusic[] = " Usage: SET MUSIC ON \n" " Enables/Disables the music on hold generator. If is\n" " not specified, then the default music on hold class will be used.\n" " Always returns 0.\n"; static char usage_dbput[] = " Usage: DATABASE PUT \n" " Adds or updates an entry in the Asterisk database for a\n" " given family, key, and value.\n" " Returns 1 if successful, 0 otherwise.\n"; static char usage_dbget[] = " Usage: DATABASE GET \n" " Retrieves an entry in the Asterisk database for a\n" " given family and key.\n" " Returns 0 if is not set. Returns 1 if \n" " is set and returns the variable in parentheses.\n" " Example return code: 200 result=1 (testvariable)\n"; static char usage_dbdel[] = " Usage: DATABASE DEL \n" " Deletes an entry in the Asterisk database for a\n" " given family and key.\n" " Returns 1 if successful, 0 otherwise.\n"; static char usage_dbdeltree[] = " Usage: DATABASE DELTREE [keytree]\n" " Deletes a family or specific keytree within a family\n" " in the Asterisk database.\n" " Returns 1 if successful, 0 otherwise.\n"; static char usage_verbose[] = " Usage: VERBOSE \n" " Sends to the console via verbose message system.\n" " is the the verbose level (1-4)\n" " Always returns 1.\n"; static char usage_getvariable[] = " Usage: GET VARIABLE \n" " Returns 0 if is not set. Returns 1 if \n" " is set and returns the variable in parentheses.\n" " example return code: 200 result=1 (testvariable)\n"; static char usage_getvariablefull[] = " Usage: GET FULL VARIABLE []\n" " Returns 0 if is not set or channel does not exist. Returns 1\n" "if is set and returns the variable in parenthesis. Understands\n" "complex variable names and builtin variables, unlike GET VARIABLE.\n" " example return code: 200 result=1 (testvariable)\n"; static char usage_setvariable[] = " Usage: SET VARIABLE \n"; static char usage_channelstatus[] = " Usage: CHANNEL STATUS []\n" " Returns the status of the specified channel.\n" " If no channel name is given the returns the status of the\n" " current channel. Return values:\n" " 0 Channel is down and available\n" " 1 Channel is down, but reserved\n" " 2 Channel is off hook\n" " 3 Digits (or equivalent) have been dialed\n" " 4 Line is ringing\n" " 5 Remote end is ringing\n" " 6 Line is up\n" " 7 Line is busy\n"; static char usage_setcallerid[] = " Usage: SET CALLERID \n" " Changes the callerid of the current channel.\n"; static char usage_exec[] = " Usage: EXEC \n" " Executes with given .\n" " Returns whatever the application returns, or -2 on failure to find application\n"; static char usage_hangup[] = " Usage: HANGUP []\n" " Hangs up the specified channel.\n" " If no channel name is given, hangs up the current channel\n"; static char usage_answer[] = " Usage: ANSWER\n" " Answers channel if not already in answer state. Returns -1 on\n" " channel failure, or 0 if successful.\n"; static char usage_waitfordigit[] = " Usage: WAIT FOR DIGIT \n" " Waits up to 'timeout' milliseconds for channel to receive a DTMF digit.\n" " Returns -1 on channel failure, 0 if no digit is received in the timeout, or\n" " the numerical value of the ascii of the digit if one is received. Use -1\n" " for the timeout value if you desire the call to block indefinitely.\n"; static char usage_sendtext[] = " Usage: SEND TEXT \"\"\n" " Sends the given text on a channel. Most channels do not support the\n" " transmission of text. Returns 0 if text is sent, or if the channel does not\n" " support text transmission. Returns -1 only on error/hangup. Text\n" " consisting of greater than one word should be placed in quotes since the\n" " command only accepts a single argument.\n"; static char usage_recvchar[] = " Usage: RECEIVE CHAR \n" " Receives a character of text on a channel. Specify timeout to be the\n" " maximum time to wait for input in milliseconds, or 0 for infinite. Most channels\n" " do not support the reception of text. Returns the decimal value of the character\n" " if one is received, or 0 if the channel does not support text reception. Returns\n" " -1 only on error/hangup.\n"; static char usage_recvtext[] = " Usage: RECEIVE TEXT \n" " Receives a string of text on a channel. Specify timeout to be the\n" " maximum time to wait for input in milliseconds, or 0 for infinite. Most channels\n" " do not support the reception of text. Returns -1 for failure or 1 for success, and the string in parentheses.\n"; static char usage_tddmode[] = " Usage: TDD MODE \n" " Enable/Disable TDD transmission/reception on a channel. Returns 1 if\n" " successful, or 0 if channel is not TDD-capable.\n"; static char usage_sendimage[] = " Usage: SEND IMAGE \n" " Sends the given image on a channel. Most channels do not support the\n" " transmission of images. Returns 0 if image is sent, or if the channel does not\n" " support image transmission. Returns -1 only on error/hangup. Image names\n" " should not include extensions.\n"; static char usage_streamfile[] = " Usage: STREAM FILE [sample offset]\n" " Send the given file, allowing playback to be interrupted by the given\n" " digits, if any. Use double quotes for the digits if you wish none to be\n" " permitted. If sample offset is provided then the audio will seek to sample\n" " offset before play starts. Returns 0 if playback completes without a digit\n" " being pressed, or the ASCII numerical value of the digit if one was pressed,\n" " or -1 on error or if the channel was disconnected. Remember, the file\n" " extension must not be included in the filename.\n"; static char usage_controlstreamfile[] = " Usage: CONTROL STREAM FILE [skipms] [ffchar] [rewchr] [pausechr]\n" " Send the given file, allowing playback to be controled by the given\n" " digits, if any. Use double quotes for the digits if you wish none to be\n" " permitted. Returns 0 if playback completes without a digit\n" " being pressed, or the ASCII numerical value of the digit if one was pressed,\n" " or -1 on error or if the channel was disconnected. Remember, the file\n" " extension must not be included in the filename.\n\n" " Note: ffchar and rewchar default to * and # respectively.\n"; static char usage_getoption[] = " Usage: GET OPTION [timeout]\n" " Behaves similar to STREAM FILE but used with a timeout option.\n"; static char usage_saynumber[] = " Usage: SAY NUMBER \n" " Say a given number, returning early if any of the given DTMF digits\n" " are received on the channel. Returns 0 if playback completes without a digit\n" " being pressed, or the ASCII numerical value of the digit if one was pressed or\n" " -1 on error/hangup.\n"; static char usage_saydigits[] = " Usage: SAY DIGITS \n" " Say a given digit string, returning early if any of the given DTMF digits\n" " are received on the channel. Returns 0 if playback completes without a digit\n" " being pressed, or the ASCII numerical value of the digit if one was pressed or\n" " -1 on error/hangup.\n"; static char usage_sayalpha[] = " Usage: SAY ALPHA \n" " Say a given character string, returning early if any of the given DTMF digits\n" " are received on the channel. Returns 0 if playback completes without a digit\n" " being pressed, or the ASCII numerical value of the digit if one was pressed or\n" " -1 on error/hangup.\n"; static char usage_saydate[] = " Usage: SAY DATE \n" " Say a given date, returning early if any of the given DTMF digits are\n" " received on the channel. is number of seconds elapsed since 00:00:00\n" " on January 1, 1970, Coordinated Universal Time (UTC). Returns 0 if playback\n" " completes without a digit being pressed, or the ASCII numerical value of the\n" " digit if one was pressed or -1 on error/hangup.\n"; static char usage_saytime[] = " Usage: SAY TIME