diff options
Diffstat (limited to 'trunk/pbx')
82 files changed, 25892 insertions, 0 deletions
diff --git a/trunk/pbx/Makefile b/trunk/pbx/Makefile new file mode 100644 index 000000000..731057b1d --- /dev/null +++ b/trunk/pbx/Makefile @@ -0,0 +1,32 @@ +# +# 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 $(ASTTOPDIR)/menuselect.makeopts $(ASTTOPDIR)/menuselect.makedeps + +MODULE_PREFIX=pbx +MENUSELECT_CATEGORY=PBX +MENUSELECT_DESCRIPTION=PBX Modules + +all: _all + +include $(ASTTOPDIR)/Makefile.moddir_rules + +ifneq ($(findstring $(OSARCH), mingw32 cygwin ),) + LIBS+= -lres_ael_share.so -lres_monitor.so +endif + +clean:: + rm -f ael/*.o + +dundi-parser.o: dundi-parser.h +dundi-parser.o: ASTCFLAGS+=-I. + +$(if $(filter pbx_dundi,$(EMBEDDED_MODS)),modules.link,pbx_dundi.so): dundi-parser.o diff --git a/trunk/pbx/ael/ael-test/ael-ntest10/extensions.ael b/trunk/pbx/ael/ael-test/ael-ntest10/extensions.ael new file mode 100644 index 000000000..4a8386ccf --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-ntest10/extensions.ael @@ -0,0 +1,131 @@ +macro 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) ; + } + } +} diff --git a/trunk/pbx/ael/ael-test/ael-ntest12/extensions.ael b/trunk/pbx/ael/ael-test/ael-ntest12/extensions.ael new file mode 100644 index 000000000..1e3183358 --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-ntest12/extensions.ael @@ -0,0 +1,13 @@ +context 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}'); + } +} diff --git a/trunk/pbx/ael/ael-test/ael-ntest22/extensions.ael b/trunk/pbx/ael/ael-test/ael-ntest22/extensions.ael new file mode 100644 index 000000000..b787f4b03 --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-ntest22/extensions.ael @@ -0,0 +1,7 @@ +#include "t1/*.ael" + +context z +{ + 123 => NoOp(hi there, z); + 124 => NoOp(hi there, z); +} diff --git a/trunk/pbx/ael/ael-test/ael-ntest22/qq.ael b/trunk/pbx/ael/ael-test/ael-ntest22/qq.ael new file mode 100644 index 000000000..c446f53fc --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-ntest22/qq.ael @@ -0,0 +1,6 @@ + + +context qq +{ + 567 => NoOp(hi there, qq); +} diff --git a/trunk/pbx/ael/ael-test/ael-ntest22/t1/a.ael b/trunk/pbx/ael/ael-test/ael-ntest22/t1/a.ael new file mode 100644 index 000000000..62e3fc588 --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-ntest22/t1/a.ael @@ -0,0 +1,4 @@ +context a +{ + 134 => NoOp(hi there, a); +} diff --git a/trunk/pbx/ael/ael-test/ael-ntest22/t1/b.ael b/trunk/pbx/ael/ael-test/ael-ntest22/t1/b.ael new file mode 100644 index 000000000..29d8d1ff4 --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-ntest22/t1/b.ael @@ -0,0 +1,6 @@ + + +context b +{ + 456 => NoOp(hithere, b); +} diff --git a/trunk/pbx/ael/ael-test/ael-ntest22/t1/c.ael b/trunk/pbx/ael/ael-test/ael-ntest22/t1/c.ael new file mode 100644 index 000000000..3c6df4bde --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-ntest22/t1/c.ael @@ -0,0 +1,13 @@ + + +context c +{ + 567 => NoOp(hi there, c); +} + +#include "t2/*.ael" + +context w +{ + 890 => NoOp(hi there, w); +} diff --git a/trunk/pbx/ael/ael-test/ael-ntest22/t2/d.ael b/trunk/pbx/ael/ael-test/ael-ntest22/t2/d.ael new file mode 100644 index 000000000..6362278f7 --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-ntest22/t2/d.ael @@ -0,0 +1,4 @@ +context d +{ + 134 => NoOp(hi there, d); +} diff --git a/trunk/pbx/ael/ael-test/ael-ntest22/t2/e.ael b/trunk/pbx/ael/ael-test/ael-ntest22/t2/e.ael new file mode 100644 index 000000000..9465c8b4e --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-ntest22/t2/e.ael @@ -0,0 +1,6 @@ + + +context e +{ + 456 => NoOp(hithere, e); +} diff --git a/trunk/pbx/ael/ael-test/ael-ntest22/t2/f.ael b/trunk/pbx/ael/ael-test/ael-ntest22/t2/f.ael new file mode 100644 index 000000000..ba15a6389 --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-ntest22/t2/f.ael @@ -0,0 +1,9 @@ +#include "qq.ael" + +context f +{ + 567 => NoOp(hi there, f); +} + +#include "t3/*.ael" + diff --git a/trunk/pbx/ael/ael-test/ael-ntest22/t3/g.ael b/trunk/pbx/ael/ael-test/ael-ntest22/t3/g.ael new file mode 100644 index 000000000..0f1ecc805 --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-ntest22/t3/g.ael @@ -0,0 +1,4 @@ +context g +{ + 134 => NoOp(hi there, g); +} diff --git a/trunk/pbx/ael/ael-test/ael-ntest22/t3/h.ael b/trunk/pbx/ael/ael-test/ael-ntest22/t3/h.ael new file mode 100644 index 000000000..f9e3ca89f --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-ntest22/t3/h.ael @@ -0,0 +1,6 @@ + + +context h +{ + 456 => NoOp(hithere, h); +} diff --git a/trunk/pbx/ael/ael-test/ael-ntest22/t3/i.ael b/trunk/pbx/ael/ael-test/ael-ntest22/t3/i.ael new file mode 100644 index 000000000..5639a1e98 --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-ntest22/t3/i.ael @@ -0,0 +1,4 @@ +context i +{ + 134 => NoOp(hi there, i); +} diff --git a/trunk/pbx/ael/ael-test/ael-ntest22/t3/j.ael b/trunk/pbx/ael/ael-test/ael-ntest22/t3/j.ael new file mode 100644 index 000000000..8dfc6c05f --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-ntest22/t3/j.ael @@ -0,0 +1,6 @@ + + +context j +{ + 567 => NoOp(hi there, j); +} diff --git a/trunk/pbx/ael/ael-test/ael-ntest9/extensions.ael b/trunk/pbx/ael/ael-test/ael-ntest9/extensions.ael new file mode 100755 index 000000000..b9762ed54 --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-ntest9/extensions.ael @@ -0,0 +1,12 @@ + +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}) ; + } +} + diff --git a/trunk/pbx/ael/ael-test/ael-test1/extensions.ael b/trunk/pbx/ael/ael-test/ael-test1/extensions.ael new file mode 100644 index 000000000..e1943f67c --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-test1/extensions.ael @@ -0,0 +1,163 @@ + +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); + }; +}; diff --git a/trunk/pbx/ael/ael-test/ael-test11/extensions.ael b/trunk/pbx/ael/ael-test/ael-test11/extensions.ael new file mode 100644 index 000000000..a6b2226f8 --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-test11/extensions.ael @@ -0,0 +1,57 @@ +context 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) ; + } + } + return; +} diff --git a/trunk/pbx/ael/ael-test/ael-test14/extensions.ael b/trunk/pbx/ael/ael-test/ael-test14/extensions.ael new file mode 100644 index 000000000..20d69134f --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-test14/extensions.ael @@ -0,0 +1,20 @@ +context test1 +{ + 10 => { + // nothing but a comment! + } + + 11 => { + switch(${somevar}) + { + case somecase: + // nothing but a comment! + break; + case somecase: + // nothing but a comment! + continue; + } + break; + } + +} diff --git a/trunk/pbx/ael/ael-test/ael-test15/extensions.ael b/trunk/pbx/ael/ael-test/ael-test15/extensions.ael new file mode 100644 index 000000000..c9cfdab96 --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-test15/extensions.ael @@ -0,0 +1,17 @@ +/* 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 */ diff --git a/trunk/pbx/ael/ael-test/ael-test16/extensions.ael b/trunk/pbx/ael/ael-test/ael-test16/extensions.ael new file mode 100644 index 000000000..5f3b2e4e9 --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-test16/extensions.ael @@ -0,0 +1,4 @@ +context real-small { + +} + diff --git a/trunk/pbx/ael/ael-test/ael-test18/extensions.ael b/trunk/pbx/ael/ael-test/ael-test18/extensions.ael new file mode 100644 index 000000000..ee03d5909 --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-test18/extensions.ael @@ -0,0 +1,40 @@ +context 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(); + }; + +} + diff --git a/trunk/pbx/ael/ael-test/ael-test19/extensions.ael b/trunk/pbx/ael/ael-test/ael-test19/extensions.ael new file mode 100644 index 000000000..07af91482 --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-test19/extensions.ael @@ -0,0 +1,377 @@ +context 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(num)=1234567890); + Set(CALLERID(name)=before fork); + Forkcdr(v); + Set(CALLERID(num)=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; +} diff --git a/trunk/pbx/ael/ael-test/ael-test2/apptest.ael2 b/trunk/pbx/ael/ael-test/ael-test2/apptest.ael2 new file mode 100644 index 000000000..c477d8531 --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-test2/apptest.ael2 @@ -0,0 +1,146 @@ +// 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(); diff --git a/trunk/pbx/ael/ael-test/ael-test2/extensions.ael b/trunk/pbx/ael/ael-test/ael-test2/extensions.ael new file mode 100644 index 000000000..176338872 --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-test2/extensions.ael @@ -0,0 +1,8 @@ +context test1 +{ + s => + { + #include "apptest.ael2"; + } +} + diff --git a/trunk/pbx/ael/ael-test/ael-test20/extensions.ael b/trunk/pbx/ael/ael-test/ael-test20/extensions.ael new file mode 100644 index 000000000..8ec219864 --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-test20/extensions.ael @@ -0,0 +1,8 @@ +context interesting { + eswitches { + Realtime/default@extensions; + IAX2/context@${CURSERVER}; + } + 13 => NoOp(LuckyNumber!); +} + diff --git a/trunk/pbx/ael/ael-test/ael-test3/extensions.ael b/trunk/pbx/ael/ael-test/ael-test3/extensions.ael new file mode 100755 index 000000000..ff1f6aea5 --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-test3/extensions.ael @@ -0,0 +1,3184 @@ +globals +{ + 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); + goto privacyManagerFailed|s|begin; + 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(num)}" = "" ) // 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(num)})=${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" + + diff --git a/trunk/pbx/ael/ael-test/ael-test3/include1.ael2 b/trunk/pbx/ael/ael-test/ael-test3/include1.ael2 new file mode 100644 index 000000000..80c562cb2 --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-test3/include1.ael2 @@ -0,0 +1,3 @@ + NoOp(Hello, this is included from include1.ael2); + #include "include2.ael2" + diff --git a/trunk/pbx/ael/ael-test/ael-test3/include2.ael2 b/trunk/pbx/ael/ael-test/ael-test3/include2.ael2 new file mode 100644 index 000000000..8d892fb0c --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-test3/include2.ael2 @@ -0,0 +1,4 @@ + NoOp(This was included from include2.ael2); + #include "include3.ael2" + #include "include4.ael2" + diff --git a/trunk/pbx/ael/ael-test/ael-test3/include3.ael2 b/trunk/pbx/ael/ael-test/ael-test3/include3.ael2 new file mode 100644 index 000000000..3c6c1e3dd --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-test3/include3.ael2 @@ -0,0 +1,2 @@ + NoOp(This is include3.ael2!); + #include "include5.ael2" diff --git a/trunk/pbx/ael/ael-test/ael-test3/include4.ael2 b/trunk/pbx/ael/ael-test/ael-test3/include4.ael2 new file mode 100644 index 000000000..7d3703a5e --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-test3/include4.ael2 @@ -0,0 +1,2 @@ + NoOp(This is include4.ael2! Isn't it cool!?!?!?!); + NoOp(4 doesn't include anything); diff --git a/trunk/pbx/ael/ael-test/ael-test3/include5.ael2 b/trunk/pbx/ael/ael-test/ael-test3/include5.ael2 new file mode 100644 index 000000000..0e18983ef --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-test3/include5.ael2 @@ -0,0 +1 @@ + NoOp(Include5.ael2 doesn't include anything, either!); diff --git a/trunk/pbx/ael/ael-test/ael-test3/telemarket_torture.ael2 b/trunk/pbx/ael/ael-test/ael-test3/telemarket_torture.ael2 new file mode 100755 index 000000000..ebd8e9f2f --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-test3/telemarket_torture.ael2 @@ -0,0 +1,812 @@ +// +// 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; +} diff --git a/trunk/pbx/ael/ael-test/ael-test4/apptest.ael2 b/trunk/pbx/ael/ael-test/ael-test4/apptest.ael2 new file mode 100644 index 000000000..c477d8531 --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-test4/apptest.ael2 @@ -0,0 +1,146 @@ +// 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(); diff --git a/trunk/pbx/ael/ael-test/ael-test4/extensions.ael b/trunk/pbx/ael/ael-test/ael-test4/extensions.ael new file mode 100644 index 000000000..838aa2489 --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-test4/extensions.ael @@ -0,0 +1,8 @@ +context test1 +{ + test2 => + { + #include "apptest.ael2"; + } +} + diff --git a/trunk/pbx/ael/ael-test/ael-test5/extensions.ael b/trunk/pbx/ael/ael-test/ael-test5/extensions.ael new file mode 100644 index 000000000..e4f703b86 --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-test5/extensions.ael @@ -0,0 +1,838 @@ +/////////////////////////////////////////////////////////////////////////////// +// 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@ixtlchochitl.zvbwu.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}@zgw1.zvbwu.edu,,wW); + Dial(SIP/${number}@zgw2.zvbwu.edu,,wW); + } else { + Dial(SIP/${number}@zgw1.zvbwu.edu); + Dial(SIP/${number}@zgw2.zvbwu.edu); + }; + return; +}; + +// 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@ixtlchochitl.zvbwu.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}@ixtlchochitl.zvbwu.edu); + Playback(im-sorry); + Playback(voice-mail-system); + Playback(down); + Congestion(10); + Hangup; +}; + +macro bvm( ext ) { + Dial(SIP/b${ext}@ixtlchochitl.zvbwu.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}); + return; +}; + +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); + }; + return; +}; + +macro checkcfb( ext ) { + if ("${info_forwardbusy}foo" = "foo") { + Wait(1); + MailboxExists(${ext}); + if ("${VMBOXEXISTSSTATUS}" = "FAILED") { + &dialout(${ext}); + Hangup; + }; + &bvm(${ext}); + Hangup; + }; + &stdexten(${info_forwardbusy}); + return; +}; + +/////////////////////////////////////////////////////////////////////////////// +// 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 { + local1; + }; + + _91XXXXXXXXXX => &dialout(${EXTEN}); + _9011. => &dialout(${EXTEN}); +}; + +context local1 { + 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 local1|${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 <xx> <y.> - Set system speed dial <xx> to digits <y.> +// *59 <xx> 0 - Delete system speed dial <xx> +// *59 <xx> - Review system speed dial <xx> +// *1xx - Dial speed dial <xx> + _*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}); + return; +}; + +context app-psd { +// *89 <xx> <y.> - Set personal speed dial <xx> to digits <y.> +// *89 <xx> 0 - Delete personal speed dial <xx> +// *89 <xx> - Review personal speed dial <xx> +// *2xx - Dial personal speed dial <xx> + _*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@ixtlchochitl.zvbwu.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@ixtlchochitl.zvbwu.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@ixtlchochitl.zvbwu.edu); + Playback(im-sorry); + Playback(voice-mail-system); + Playback(down); + Playback(extra/pls-try-call-later); + Congestion(10); + Hangup; + }; +}; diff --git a/trunk/pbx/ael/ael-test/ael-test6/extensions.ael b/trunk/pbx/ael/ael-test/ael-test6/extensions.ael new file mode 100644 index 000000000..13ebf67fd --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-test6/extensions.ael @@ -0,0 +1,833 @@ +/////////////////////////////////////////////////////////////////////////////// +// 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 { + local1; + }; + + _91XXXXXXXXXX => &dialout(${EXTEN}); + _9011. => &dialout(${EXTEN}); +}; + +context local1 { + 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 local1|${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 <xx> <y.> - Set system speed dial <xx> to digits <y.> +// *59 <xx> 0 - Delete system speed dial <xx> +// *59 <xx> - Review system speed dial <xx> +// *1xx - Dial speed dial <xx> + _*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 <xx> <y.> - Set personal speed dial <xx> to digits <y.> +// *89 <xx> 0 - Delete personal speed dial <xx> +// *89 <xx> - Review personal speed dial <xx> +// *2xx - Dial personal speed dial <xx> + _*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; + }; +}; diff --git a/trunk/pbx/ael/ael-test/ael-test7/extensions.ael b/trunk/pbx/ael/ael-test/ael-test7/extensions.ael new file mode 100644 index 000000000..27ed374f5 --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-test7/extensions.ael @@ -0,0 +1,460 @@ +// +// 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(num)}" = "1") { + Set(CALLERID(num)=7322271653); + } else { + Set(CALLERID(num)=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(num)}); + 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(num)=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(num)=1400898); + Dial(${TARIOPROTO}/${EXTEN:3}@${TARIO},60,T); + }; + _0157. => { //TARIO outbound Russia + Set(CALLERID(name)=Sergey Okhapkin); + Set(CALLERID(num)=1400898); + Dial(${TARIOPROTO}/8${EXTEN:4}@${TARIO},60,T); + }; +// _015. => { //TARIO outbound international +// CALLERID(name)="Sergey Okhapkin"; +// CALLERID(num)=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(num)}" = "sunrocketcom") + Set(CALLERID(num)=); + 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(num)}" = "7322271677") + Set(CALLERID(num)=); + jump 3@default; + }; +}; + +context tario.net-in { + _X. => { + Set(CALLERID(name)=); + if("${CALLERID(num):-11:1}" = "8") + Set(CALLERID(num)=7${CALLERID(num):1}); + if("${SIP_HEADER(To)}" = "<sip:2271677@sipnet.ru>") { + jump 3@default; + } else if("${SIP_HEADER(To)}" = "<sip:2271653@sipnet.ru>") { + 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(num)=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); +}; + diff --git a/trunk/pbx/ael/ael-test/ael-test8/extensions.ael b/trunk/pbx/ael/ael-test/ael-test8/extensions.ael new file mode 100644 index 000000000..17bc74e6f --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-test8/extensions.ael @@ -0,0 +1,27 @@ +context 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(); + }; + +} + diff --git a/trunk/pbx/ael/ael-test/ael-vtest13/extensions.ael b/trunk/pbx/ael/ael-test/ael-vtest13/extensions.ael new file mode 100755 index 000000000..b7e953e62 --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-vtest13/extensions.ael @@ -0,0 +1,3183 @@ +globals +{ + 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(num)}" = "" ) // 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(num)})=${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" + + diff --git a/trunk/pbx/ael/ael-test/ael-vtest13/include1.ael2 b/trunk/pbx/ael/ael-test/ael-vtest13/include1.ael2 new file mode 100644 index 000000000..80c562cb2 --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-vtest13/include1.ael2 @@ -0,0 +1,3 @@ + NoOp(Hello, this is included from include1.ael2); + #include "include2.ael2" + diff --git a/trunk/pbx/ael/ael-test/ael-vtest13/include2.ael2 b/trunk/pbx/ael/ael-test/ael-vtest13/include2.ael2 new file mode 100644 index 000000000..8d892fb0c --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-vtest13/include2.ael2 @@ -0,0 +1,4 @@ + NoOp(This was included from include2.ael2); + #include "include3.ael2" + #include "include4.ael2" + diff --git a/trunk/pbx/ael/ael-test/ael-vtest13/include3.ael2 b/trunk/pbx/ael/ael-test/ael-vtest13/include3.ael2 new file mode 100644 index 000000000..3c6c1e3dd --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-vtest13/include3.ael2 @@ -0,0 +1,2 @@ + NoOp(This is include3.ael2!); + #include "include5.ael2" diff --git a/trunk/pbx/ael/ael-test/ael-vtest13/include4.ael2 b/trunk/pbx/ael/ael-test/ael-vtest13/include4.ael2 new file mode 100644 index 000000000..7d3703a5e --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-vtest13/include4.ael2 @@ -0,0 +1,2 @@ + NoOp(This is include4.ael2! Isn't it cool!?!?!?!); + NoOp(4 doesn't include anything); diff --git a/trunk/pbx/ael/ael-test/ael-vtest13/include5.ael2 b/trunk/pbx/ael/ael-test/ael-vtest13/include5.ael2 new file mode 100644 index 000000000..0e18983ef --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-vtest13/include5.ael2 @@ -0,0 +1 @@ + NoOp(Include5.ael2 doesn't include anything, either!); diff --git a/trunk/pbx/ael/ael-test/ael-vtest13/telemarket_torture.ael2 b/trunk/pbx/ael/ael-test/ael-vtest13/telemarket_torture.ael2 new file mode 100755 index 000000000..ebd8e9f2f --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-vtest13/telemarket_torture.ael2 @@ -0,0 +1,812 @@ +// +// 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; +} diff --git a/trunk/pbx/ael/ael-test/ael-vtest17/extensions.ael b/trunk/pbx/ael/ael-test/ael-vtest17/extensions.ael new file mode 100644 index 000000000..d13fe99d7 --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-vtest17/extensions.ael @@ -0,0 +1,116 @@ +context 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; +} diff --git a/trunk/pbx/ael/ael-test/ael-vtest21/extensions.ael b/trunk/pbx/ael/ael-test/ael-vtest21/extensions.ael new file mode 100644 index 000000000..95f25302a --- /dev/null +++ b/trunk/pbx/ael/ael-test/ael-vtest21/extensions.ael @@ -0,0 +1,14 @@ +globals { + AXLHAFT=wow-to-the-tenth-power; + JibberWorthy=zinger3; + OFFICE_CODE=503; +} + +context from-enum { + + _${OFFICE_CODE}XXXX => { + Answer(); + goto ${EXTEN:3}|1; + } +} + diff --git a/trunk/pbx/ael/ael-test/ref.ael-ntest10 b/trunk/pbx/ael/ael-test/ref.ael-ntest10 new file mode 100644 index 000000000..9eb8ac989 --- /dev/null +++ b/trunk/pbx/ael/ael-test/ref.ael-ntest10 @@ -0,0 +1,171 @@ + +(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:113 func: pbx_load_module Starting AEL load process. +LOG: lev:2 file:pbx_ael.c line:126 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1-4: The macro endsess does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 6-9: The macro nullchk does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 11-26: The macro endcall does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:2284 func: check_switch_expr Warning: file ./extensions.ael, line 13-13: A default case was automatically added to the switch. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 28-44: The macro endcall2 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:2284 func: check_switch_expr Warning: file ./extensions.ael, line 36-36: A default case was automatically added to the switch. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 46-68: The macro endcall3 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:2284 func: check_switch_expr Warning: file ./extensions.ael, line 48-48: A default case was automatically added to the switch. +LOG: lev:3 file:pval.c line:2284 func: check_switch_expr Warning: file ./extensions.ael, line 60-60: A default case was automatically added to the switch. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 70-96: The macro endcall4 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:2284 func: check_switch_expr Warning: file ./extensions.ael, line 72-72: A default case was automatically added to the switch. +LOG: lev:3 file:pval.c line:2284 func: check_switch_expr Warning: file ./extensions.ael, line 84-84: A default case was automatically added to the switch. +LOG: lev:3 file:pval.c line:2284 func: check_switch_expr Warning: file ./extensions.ael, line 87-87: A default case was automatically added to the switch. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 98-131: The macro endcall5 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:2284 func: check_switch_expr Warning: file ./extensions.ael, line 106-106: A default case was automatically added to the switch. +LOG: lev:3 file:pval.c line:2284 func: check_switch_expr Warning: file ./extensions.ael, line 119-119: A default case was automatically added to the switch. +LOG: lev:3 file:pval.c line:2284 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:129 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. +Executed ast_context_create(conts, name=endsess, registrar=pbx_ael); +Executed ast_context_create(conts, name=nullchk, registrar=pbx_ael); +Executed ast_context_create(conts, name=endcall, registrar=pbx_ael); +Executed ast_context_create(conts, name=endcall2, registrar=pbx_ael); +Executed ast_context_create(conts, name=endcall3, registrar=pbx_ael); +Executed ast_context_create(conts, name=endcall4, registrar=pbx_ael); +Executed ast_context_create(conts, name=endcall5, registrar=pbx_ael); +Executed ast_add_extension2(context=endsess, rep=0, exten=s, priority=1, label=(null), callerid=(null), appl=NoOp, data=hithere, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=endsess, rep=0, exten=s, priority=2, label=(null), callerid=(null), appl=Return, data=, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=nullchk, rep=0, exten=s, priority=1, label=(null), callerid=(null), appl=Set, data=LOCAL(type)=${ARG1}, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=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=nullchk, rep=0, exten=s, priority=3, label=(null), callerid=(null), appl=Return, data=, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=endcall, rep=0, exten=s, priority=1, label=(null), callerid=(null), appl=Set, data=LOCAL(type)=${ARG1}, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=endcall, rep=0, exten=s, priority=2, label=(null), callerid=(null), appl=Goto, data=sw-1-${type},10, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=endcall, rep=0, exten=s, priority=3, label=(null), callerid=(null), appl=NoOp, data=Finish switch-endcall-1, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=endcall, rep=0, exten=s, priority=4, label=(null), callerid=(null), appl=Return, data=, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=endcall, rep=0, exten=_sw-1-., priority=10, label=(null), callerid=(null), appl=Goto, data=s,3, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=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=endcall, rep=0, exten=sw-1-out, priority=10, label=(null), callerid=(null), appl=Gosub, data=nullchk,s,1(callid), FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=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=endcall, rep=0, exten=sw-1-out, priority=12, label=(null), callerid=(null), appl=Gosub, data=endsess,s,1, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=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=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=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=endcall, rep=0, exten=sw-1-out, priority=16, label=(null), callerid=(null), appl=Goto, data=s,3, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=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=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=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=endcall2, rep=0, exten=s, priority=1, label=(null), callerid=(null), appl=Set, data=LOCAL(type)=${ARG1}, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=endcall2, rep=0, exten=s, priority=2, label=(null), callerid=(null), appl=Goto, data=sw-3-${type},10, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=endcall2, rep=0, exten=s, priority=3, label=(null), callerid=(null), appl=NoOp, data=Finish switch-endcall2-3, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=endcall2, rep=0, exten=s, priority=4, label=(null), callerid=(null), appl=Return, data=, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=endcall2, rep=0, exten=_sw-3-., priority=10, label=(null), callerid=(null), appl=Goto, data=s,3, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=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=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=endcall2, rep=0, exten=sw-3-out2, priority=11, label=(null), callerid=(null), appl=Goto, data=s,3, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=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=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=endcall2, rep=0, exten=sw-3-out, priority=10, label=(null), callerid=(null), appl=Gosub, data=nullchk,s,1(callid), FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=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=endcall2, rep=0, exten=sw-3-out, priority=12, label=(null), callerid=(null), appl=Gosub, data=endsess,s,1, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=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=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=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=endcall3, rep=0, exten=s, priority=1, label=(null), callerid=(null), appl=Set, data=LOCAL(type)=${ARG1}, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=endcall3, rep=0, exten=s, priority=2, label=(null), callerid=(null), appl=Goto, data=sw-5-${type},10, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=endcall3, rep=0, exten=s, priority=3, label=(null), callerid=(null), appl=NoOp, data=Finish switch-endcall3-5, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=endcall3, rep=0, exten=s, priority=4, label=(null), callerid=(null), appl=GotoIf, data=$[${testnotnull}]?5:6, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=endcall3, rep=0, exten=s, priority=5, label=(null), callerid=(null), appl=Goto, data=sw-8-out,ptr1, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=endcall3, rep=0, exten=s, priority=6, label=(null), callerid=(null), appl=NoOp, data=Finish if-endcall3-7, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=endcall3, rep=0, exten=s, priority=7, label=(null), callerid=(null), appl=Goto, data=sw-8-${type},10, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=endcall3, rep=0, exten=s, priority=8, label=(null), callerid=(null), appl=NoOp, data=Finish switch-endcall3-8, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=endcall3, rep=0, exten=s, priority=9, label=(null), callerid=(null), appl=Return, data=, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=endcall3, rep=0, exten=_sw-8-., priority=10, label=(null), callerid=(null), appl=Goto, data=s,8, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=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=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=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=endcall3, rep=0, exten=sw-8-out, priority=12, label=(null), callerid=(null), appl=Goto, data=s,8, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=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=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=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=endcall3, rep=0, exten=_sw-5-., priority=10, label=(null), callerid=(null), appl=Goto, data=s,3, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=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=endcall3, rep=0, exten=sw-5-out, priority=10, label=(null), callerid=(null), appl=Gosub, data=nullchk,s,1(callid), FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=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=endcall3, rep=0, exten=sw-5-out, priority=12, label=(null), callerid=(null), appl=Gosub, data=endsess,s,1, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=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=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=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=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=endcall4, rep=0, exten=s, priority=1, label=(null), callerid=(null), appl=Set, data=LOCAL(type)=${ARG1}, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=endcall4, rep=0, exten=s, priority=2, label=(null), callerid=(null), appl=Goto, data=sw-10-${type},10, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=endcall4, rep=0, exten=s, priority=3, label=(null), callerid=(null), appl=NoOp, data=Finish switch-endcall4-10, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=endcall4, rep=0, exten=s, priority=4, label=(null), callerid=(null), appl=GotoIf, data=$[${testnotnull}]?5:6, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=endcall4, rep=0, exten=s, priority=5, label=(null), callerid=(null), appl=Goto, data=sw-14-in,ptr1, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=endcall4, rep=0, exten=s, priority=6, label=(null), callerid=(null), appl=NoOp, data=Finish if-endcall4-12, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=endcall4, rep=0, exten=s, priority=7, label=(null), callerid=(null), appl=Goto, data=sw-13-${type},10, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=endcall4, rep=0, exten=s, priority=8, label=(null), callerid=(null), appl=NoOp, data=Finish switch-endcall4-13, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=endcall4, rep=0, exten=s, priority=9, label=(null), callerid=(null), appl=Return, data=, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=endcall4, rep=0, exten=_sw-13-., priority=10, label=(null), callerid=(null), appl=Goto, data=s,8, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=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=endcall4, rep=0, exten=sw-13-out, priority=10, label=(null), callerid=(null), appl=Goto, data=sw-14-${type},10, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=endcall4, rep=0, exten=sw-13-out, priority=11, label=(null), callerid=(null), appl=NoOp, data=Finish switch-sw-endcall4-out-13-14, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=endcall4, rep=0, exten=sw-13-out, priority=12, label=(null), callerid=(null), appl=Goto, data=sw-13-.,10, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=endcall4, rep=0, exten=_sw-14-., priority=10, label=(null), callerid=(null), appl=Goto, data=sw-13-out,11, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=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=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=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=endcall4, rep=0, exten=sw-14-in, priority=12, label=(null), callerid=(null), appl=Goto, data=sw-13-out,11, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=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=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=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=endcall4, rep=0, exten=_sw-10-., priority=10, label=(null), callerid=(null), appl=Goto, data=s,3, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=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=endcall4, rep=0, exten=sw-10-out, priority=10, label=(null), callerid=(null), appl=Gosub, data=nullchk,s,1(callid), FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=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=endcall4, rep=0, exten=sw-10-out, priority=12, label=(null), callerid=(null), appl=Gosub, data=endsess,s,1, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=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=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=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=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=endcall5, rep=0, exten=s, priority=1, label=(null), callerid=(null), appl=Set, data=LOCAL(type)=${ARG1}, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=endcall5, rep=0, exten=s, priority=2, label=(null), callerid=(null), appl=Goto, data=sw-16-${type},10, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=endcall5, rep=0, exten=s, priority=3, label=(null), callerid=(null), appl=NoOp, data=Finish switch-endcall5-16, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=endcall5, rep=0, exten=s, priority=4, label=(null), callerid=(null), appl=GotoIf, data=$[${testnotnull}]?5:6, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=endcall5, rep=0, exten=s, priority=5, label=(null), callerid=(null), appl=Goto, data=sw-21-in,ptr1, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=endcall5, rep=0, exten=s, priority=6, label=(null), callerid=(null), appl=NoOp, data=Finish if-endcall5-19, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=endcall5, rep=0, exten=s, priority=7, label=(null), callerid=(null), appl=Goto, data=sw-20-${type},10, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=endcall5, rep=0, exten=s, priority=8, label=(null), callerid=(null), appl=NoOp, data=Finish switch-endcall5-20, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=endcall5, rep=0, exten=s, priority=9, label=(null), callerid=(null), appl=Return, data=, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=endcall5, rep=0, exten=_sw-20-., priority=10, label=(null), callerid=(null), appl=Goto, data=s,8, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=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=endcall5, rep=0, exten=sw-20-out, priority=10, label=(null), callerid=(null), appl=Goto, data=sw-21-${type},10, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=endcall5, rep=0, exten=sw-20-out, priority=11, label=(null), callerid=(null), appl=NoOp, data=Finish switch-sw-endcall5-out-20-21, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=endcall5, rep=0, exten=sw-20-out, priority=12, label=(null), callerid=(null), appl=Goto, data=sw-20-.,10, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=endcall5, rep=0, exten=_sw-21-., priority=10, label=(null), callerid=(null), appl=Goto, data=sw-20-out,11, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=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=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=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=endcall5, rep=0, exten=sw-21-in, priority=12, label=(null), callerid=(null), appl=Goto, data=sw-20-out,11, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=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=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=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=endcall5, rep=0, exten=_sw-16-., priority=10, label=(null), callerid=(null), appl=Goto, data=s,3, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=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=endcall5, rep=0, exten=sw-16-in, priority=10, label=(null), callerid=(null), appl=Gosub, data=nullchk,s,1(callid), FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=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=endcall5, rep=0, exten=sw-16-in, priority=12, label=(null), callerid=(null), appl=Gosub, data=endsess,s,1, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=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=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=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=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=endcall5, rep=0, exten=sw-16-out, priority=10, label=(null), callerid=(null), appl=Gosub, data=nullchk,s,1(callid), FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=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=endcall5, rep=0, exten=sw-16-out, priority=12, label=(null), callerid=(null), appl=Gosub, data=endsess,s,1, FREE, registrar=pbx_ael); +Executed ast_add_extension2(context=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=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=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:131 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:134 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:137 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. +LOG: lev:4 file:ael2_parse line:531 func: main 7 contexts, 37 extensions, 131 priorities diff --git a/trunk/pbx/ael/ael-test/ref.ael-ntest12 b/trunk/pbx/ael/ael-test/ref.ael-ntest12 new file mode 100644 index 000000000..184ce2d6c --- /dev/null +++ b/trunk/pbx/ael/ael-test/ref.ael-ntest12 @@ -0,0 +1,30 @@ + +(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:113 func: pbx_load_module Starting AEL load process. +LOG: lev:2 file:pbx_ael.c line:126 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:129 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. +Executed ast_context_find_or_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:131 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:134 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:137 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. +LOG: lev:4 file:ael2_parse line:531 func: main 1 contexts, 2 extensions, 12 priorities diff --git a/trunk/pbx/ael/ael-test/ref.ael-ntest22 b/trunk/pbx/ael/ael-test/ref.ael-ntest22 new file mode 100644 index 000000000..c9317f64e --- /dev/null +++ b/trunk/pbx/ael/ael-test/ref.ael-ntest22 @@ -0,0 +1,54 @@ + +(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:113 func: pbx_load_module Starting AEL load process. +LOG: lev:2 file:ael.flex line:662 func: setup_filestack --Read in included file ././t1/a.ael, 41 chars +LOG: lev:2 file:ael.flex line:662 func: setup_filestack --Read in included file ././t1/b.ael, 42 chars +LOG: lev:2 file:ael.flex line:662 func: setup_filestack --Read in included file ././t1/c.ael, 106 chars +LOG: lev:2 file:ael.flex line:662 func: setup_filestack --Read in included file ././t2/d.ael, 41 chars +LOG: lev:2 file:ael.flex line:662 func: setup_filestack --Read in included file ././t2/e.ael, 42 chars +LOG: lev:2 file:ael.flex line:662 func: setup_filestack --Read in included file ././t2/f.ael, 82 chars +LOG: lev:2 file:ael.flex line:662 func: setup_filestack --Read in included file ././qq.ael, 45 chars +LOG: lev:2 file:ael.flex line:662 func: setup_filestack --Read in included file ././t3/g.ael, 41 chars +LOG: lev:2 file:ael.flex line:662 func: setup_filestack --Read in included file ././t3/h.ael, 42 chars +LOG: lev:2 file:ael.flex line:662 func: setup_filestack --Read in included file ././t3/i.ael, 41 chars +LOG: lev:2 file:ael.flex line:662 func: setup_filestack --Read in included file ././t3/j.ael, 43 chars +LOG: lev:2 file:pbx_ael.c line:126 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:129 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. +Executed ast_context_find_or_create(conts, name=a, registrar=pbx_ael); +Executed ast_context_find_or_create(conts, name=b, registrar=pbx_ael); +Executed ast_context_find_or_create(conts, name=c, registrar=pbx_ael); +Executed ast_context_find_or_create(conts, name=d, registrar=pbx_ael); +Executed ast_context_find_or_create(conts, name=e, registrar=pbx_ael); +Executed ast_context_find_or_create(conts, name=qq, registrar=pbx_ael); +Executed ast_context_find_or_create(conts, name=f, registrar=pbx_ael); +Executed ast_context_find_or_create(conts, name=g, registrar=pbx_ael); +Executed ast_context_find_or_create(conts, name=h, registrar=pbx_ael); +Executed ast_context_find_or_create(conts, name=i, registrar=pbx_ael); +Executed ast_context_find_or_create(conts, name=j, registrar=pbx_ael); +Executed ast_context_find_or_create(conts, name=w, registrar=pbx_ael); +Executed ast_context_find_or_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:131 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:134 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:137 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. +LOG: lev:4 file:ael2_parse line:531 func: main 13 contexts, 13 extensions, 14 priorities diff --git a/trunk/pbx/ael/ael-test/ref.ael-ntest9 b/trunk/pbx/ael/ael-test/ref.ael-ntest9 new file mode 100644 index 000000000..e40790e59 --- /dev/null +++ b/trunk/pbx/ael/ael-test/ref.ael-ntest9 @@ -0,0 +1,23 @@ + +(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:113 func: pbx_load_module Starting AEL load process. +LOG: lev:2 file:pbx_ael.c line:126 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:129 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. +Executed ast_context_find_or_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:131 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:134 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:137 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. +LOG: lev:4 file:ael2_parse line:531 func: main 1 contexts, 1 extensions, 4 priorities diff --git a/trunk/pbx/ael/ael-test/ref.ael-test1 b/trunk/pbx/ael/ael-test/ref.ael-test1 new file mode 100644 index 000000000..5538514fe --- /dev/null +++ b/trunk/pbx/ael/ael-test/ref.ael-test1 @@ -0,0 +1,18 @@ + +(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:113 func: pbx_load_module Starting AEL load process. +LOG: lev:2 file:pbx_ael.c line:126 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 2-16: The macro testdial does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 18-25: The macro exten-gen does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:1362 func: check_goto Warning: file ./extensions.ael, line 21-21: It's bad form to have a goto in a macro to a target outside the macro! +LOG: lev:3 file:pval.c line:1362 func: check_goto Warning: file ./extensions.ael, line 23-23: It's bad form to have a goto in a macro to a target outside the macro! +LOG: lev:3 file:pval.c line:2284 func: check_switch_expr Warning: file ./extensions.ael, line 58-58: A default case was automatically added to the switch. +LOG: lev:3 file:pval.c line:922 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:pval.c line:880 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:129 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:131 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:134 func: pbx_load_module AEL load process: merged config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:137 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. +LOG: lev:4 file:ael2_parse line:531 func: main 5 contexts, 16 extensions, 157 priorities diff --git a/trunk/pbx/ael/ael-test/ref.ael-test11 b/trunk/pbx/ael/ael-test/ref.ael-test11 new file mode 100644 index 000000000..c47f2b8bf --- /dev/null +++ b/trunk/pbx/ael/ael-test/ref.ael-test11 @@ -0,0 +1,13 @@ + +(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:113 func: pbx_load_module Starting AEL load process. +LOG: lev:2 file:pbx_ael.c line:126 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. +LOG: lev:4 file:pval.c line:1147 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:pval.c line:2284 func: check_switch_expr Warning: file ./extensions.ael, line 32-32: A default case was automatically added to the switch. +LOG: lev:3 file:pval.c line:2284 func: check_switch_expr Warning: file ./extensions.ael, line 44-44: A default case was automatically added to the switch. +LOG: lev:3 file:pval.c line:2284 func: check_switch_expr Warning: file ./extensions.ael, line 47-47: A default case was automatically added to the switch. +LOG: lev:4 file:pval.c line:1147 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:139 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:531 func: main 0 contexts, 0 extensions, 0 priorities diff --git a/trunk/pbx/ael/ael-test/ref.ael-test14 b/trunk/pbx/ael/ael-test/ref.ael-test14 new file mode 100644 index 000000000..2cd4ed1b1 --- /dev/null +++ b/trunk/pbx/ael/ael-test/ref.ael-test14 @@ -0,0 +1,11 @@ + +(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:113 func: pbx_load_module Starting AEL load process. +LOG: lev:2 file:pbx_ael.c line:126 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. +LOG: lev:3 file:pval.c line:2284 func: check_switch_expr Warning: file ./extensions.ael, line 13-13: A default case was automatically added to the switch. +LOG: lev:4 file:pval.c line:1071 func: check_continue Error: file ./extensions.ael, line 15-15: 'continue' not in 'for' or 'while' statement! +LOG: lev:4 file:pval.c line:1052 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:139 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:531 func: main 0 contexts, 0 extensions, 0 priorities diff --git a/trunk/pbx/ael/ael-test/ref.ael-test15 b/trunk/pbx/ael/ael-test/ref.ael-test15 new file mode 100644 index 000000000..995068563 --- /dev/null +++ b/trunk/pbx/ael/ael-test/ref.ael-test15 @@ -0,0 +1,12 @@ + +(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:113 func: pbx_load_module Starting AEL load process. +LOG: lev:2 file:pbx_ael.c line:126 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:129 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. +LOG: lev:3 file:pval.c line:4070 func: ast_compile_ael2 Warning: file ./extensions.ael, line 8-13: Empty Extension! +LOG: lev:2 file:pbx_ael.c line:131 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:134 func: pbx_load_module AEL load process: merged config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:137 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. +LOG: lev:4 file:ael2_parse line:531 func: main 1 contexts, 0 extensions, 0 priorities diff --git a/trunk/pbx/ael/ael-test/ref.ael-test16 b/trunk/pbx/ael/ael-test/ref.ael-test16 new file mode 100644 index 000000000..f43257d46 --- /dev/null +++ b/trunk/pbx/ael/ael-test/ref.ael-test16 @@ -0,0 +1,12 @@ + +(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:113 func: pbx_load_module Starting AEL load process. +LOG: lev:2 file:pbx_ael.c line:126 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:129 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. +LOG: lev:3 file:pval.c line:3786 func: add_extensions This file is Empty! +LOG: lev:2 file:pbx_ael.c line:131 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:134 func: pbx_load_module AEL load process: merged config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:137 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. +LOG: lev:4 file:ael2_parse line:531 func: main 1 contexts, 0 extensions, 0 priorities diff --git a/trunk/pbx/ael/ael-test/ref.ael-test18 b/trunk/pbx/ael/ael-test/ref.ael-test18 new file mode 100644 index 000000000..072135ef2 --- /dev/null +++ b/trunk/pbx/ael/ael-test/ref.ael-test18 @@ -0,0 +1,11 @@ + +(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:113 func: pbx_load_module Starting AEL load process. +LOG: lev:2 file:pbx_ael.c line:126 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:129 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:131 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:134 func: pbx_load_module AEL load process: merged config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:137 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. +LOG: lev:4 file:ael2_parse line:531 func: main 1 contexts, 7 extensions, 27 priorities diff --git a/trunk/pbx/ael/ael-test/ref.ael-test19 b/trunk/pbx/ael/ael-test/ref.ael-test19 new file mode 100644 index 000000000..e093d75e5 --- /dev/null +++ b/trunk/pbx/ael/ael-test/ref.ael-test19 @@ -0,0 +1,18 @@ + +(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:113 func: pbx_load_module Starting AEL load process. +LOG: lev:2 file:pbx_ael.c line:126 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. +LOG: lev:3 file:pval.c line:2300 func: check_context_names Warning: 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:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 71-175: The macro std-priv-exten does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:2284 func: check_switch_expr Warning: file ./extensions.ael, line 245-246: A default case was automatically added to the switch. +LOG: lev:3 file:pval.c line:2426 func: check_pval_item Warning: file ./extensions.ael, line 312-312: macro call to non-existent funcA! (Not even in the extensions.conf stuff!) +LOG: lev:3 file:pval.c line:2426 func: check_pval_item Warning: file ./extensions.ael, line 313-313: macro call to non-existent funcD! (Not even in the extensions.conf stuff!) +LOG: lev:3 file:pval.c line:1346 func: check_goto Warning: file ./extensions.ael, line 319-319: goto: Couldn't find goto target test5|s|1, not even in extensions.conf! +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 363-366: The macro dialoutpstn does not end with a return; I will insert one. +LOG: lev:2 file:pbx_ael.c line:129 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:131 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:134 func: pbx_load_module AEL load process: merged config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:137 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. +LOG: lev:4 file:ael2_parse line:531 func: main 13 contexts, 57 extensions, 188 priorities diff --git a/trunk/pbx/ael/ael-test/ref.ael-test2 b/trunk/pbx/ael/ael-test/ref.ael-test2 new file mode 100644 index 000000000..892b63464 --- /dev/null +++ b/trunk/pbx/ael/ael-test/ref.ael-test2 @@ -0,0 +1,28 @@ + +(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:113 func: pbx_load_module Starting AEL load process. +LOG: lev:2 file:ael.flex line:662 func: setup_filestack --Read in included file ././apptest.ael2, 3474 chars +LOG: lev:3 file:ael.y line:546 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:126 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. +LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 35-35: application call to EndWhile affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 37-37: application call to ExecIf affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 38-38: application call to ExecIfTime affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 44-44: application call to Gosub affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 45-45: application call to GosubIf affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:1346 func: check_goto Warning: file ././apptest.ael2, line 46-46: goto: Couldn't find goto target cont|exten|prior, not even in extensions.conf! +LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 47-47: application call to GotoIf affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 48-48: application call to GotoIfTime affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 58-58: application call to Macro affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2512 func: check_pval_item Warning: file ././apptest.ael2, line 59-59: I am converting the MacroExit call here to a return statement. +LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 60-60: application call to MacroIf affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 85-85: application call to Random affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 94-94: application call to Return affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 119-119: application call to StackPop affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 141-141: application call to While affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:2 file:pbx_ael.c line:129 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:131 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:134 func: pbx_load_module AEL load process: merged config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:137 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. +LOG: lev:4 file:ael2_parse line:531 func: main 1 contexts, 1 extensions, 142 priorities diff --git a/trunk/pbx/ael/ael-test/ref.ael-test20 b/trunk/pbx/ael/ael-test/ref.ael-test20 new file mode 100644 index 000000000..065281f94 --- /dev/null +++ b/trunk/pbx/ael/ael-test/ref.ael-test20 @@ -0,0 +1,11 @@ + +(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:113 func: pbx_load_module Starting AEL load process. +LOG: lev:2 file:pbx_ael.c line:126 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:129 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:131 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:134 func: pbx_load_module AEL load process: merged config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:137 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. +LOG: lev:4 file:ael2_parse line:531 func: main 1 contexts, 1 extensions, 1 priorities diff --git a/trunk/pbx/ael/ael-test/ref.ael-test3 b/trunk/pbx/ael/ael-test/ref.ael-test3 new file mode 100644 index 000000000..5ff89ef8b --- /dev/null +++ b/trunk/pbx/ael/ael-test/ref.ael-test3 @@ -0,0 +1,99 @@ + +(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:113 func: pbx_load_module Starting AEL load process. +LOG: lev:2 file:ael.flex line:662 func: setup_filestack --Read in included file ././include1.ael2, 78 chars +LOG: lev:2 file:ael.flex line:662 func: setup_filestack --Read in included file ././include2.ael2, 98 chars +LOG: lev:2 file:ael.flex line:662 func: setup_filestack --Read in included file ././include3.ael2, 57 chars +LOG: lev:2 file:ael.flex line:662 func: setup_filestack --Read in included file ././include5.ael2, 56 chars +LOG: lev:2 file:ael.flex line:662 func: setup_filestack --Read in included file ././include4.ael2, 87 chars +LOG: lev:2 file:ael.flex line:662 func: setup_filestack --Read in included file ././telemarket_torture.ael2, 28036 chars +LOG: lev:2 file:pbx_ael.c line:126 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 14-34: The macro std-exten does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:1362 func: check_goto Warning: file ./extensions.ael, line 17-17: It's bad form to have a goto in a macro to a target outside the macro! +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 36-59: The macro std-priv-exten_1 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 62-85: The macro std-priv-exten_2 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 88-111: The macro std-priv-exten_3 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 114-137: The macro std-priv-exten_4 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 140-163: The macro std-priv-exten_5 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 166-189: The macro std-priv-exten_6 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 192-215: The macro std-priv-exten_7 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 218-241: The macro std-priv-exten_8 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 244-267: The macro std-priv-exten_9 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 270-293: The macro std-priv-exten_10 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 296-319: The macro std-priv-exten_11 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 322-345: The macro std-priv-exten_12 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 348-371: The macro std-priv-exten_13 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 374-397: The macro std-priv-exten_14 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 400-423: The macro std-priv-exten_15 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 426-449: The macro std-priv-exten_16 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 452-475: The macro std-priv-exten_17 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 478-501: The macro std-priv-exten_18 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 504-527: The macro std-priv-exten_19 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 530-553: The macro std-priv-exten_20 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 556-579: The macro std-priv-exten_21 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 582-605: The macro std-priv-exten_22 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 608-631: The macro std-priv-exten_23 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 634-657: The macro std-priv-exten_24 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 660-683: The macro std-priv-exten_25 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 686-709: The macro std-priv-exten_26 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 712-735: The macro std-priv-exten_27 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 738-761: The macro std-priv-exten_28 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 764-787: The macro std-priv-exten_29 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 790-813: The macro std-priv-exten_30 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 816-839: The macro std-priv-exten_31 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 842-865: The macro std-priv-exten_32 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 868-891: The macro std-priv-exten_33 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 894-917: The macro std-priv-exten_34 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 920-943: The macro std-priv-exten_35 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 946-969: The macro std-priv-exten_36 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 972-995: The macro std-priv-exten_37 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 998-1021: The macro std-priv-exten_38 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1024-1047: The macro std-priv-exten_39 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1050-1073: The macro std-priv-exten_40 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1076-1099: The macro std-priv-exten_41 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1102-1125: The macro std-priv-exten_42 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1128-1151: The macro std-priv-exten_43 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1154-1177: The macro std-priv-exten_44 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1180-1203: The macro std-priv-exten_45 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1206-1229: The macro std-priv-exten_46 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1232-1255: The macro std-priv-exten_47 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1258-1281: The macro std-priv-exten_48 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1284-1307: The macro std-priv-exten_49 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1310-1333: The macro std-priv-exten_50 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1336-1359: The macro std-priv-exten_51 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1362-1385: The macro std-priv-exten_52 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1388-1411: The macro std-priv-exten_53 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1414-1437: The macro std-priv-exten_54 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1440-1463: The macro std-priv-exten_55 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1466-1489: The macro std-priv-exten_56 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1492-1515: The macro std-priv-exten_57 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1518-1541: The macro std-priv-exten_58 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1544-1567: The macro std-priv-exten_59 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1570-1593: The macro std-priv-exten_60 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1596-1619: The macro std-priv-exten_61 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1622-1645: The macro std-priv-exten_62 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1648-1671: The macro std-priv-exten_63 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1674-1697: The macro std-priv-exten_64 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1700-1723: The macro std-priv-exten_65 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1726-1749: The macro std-priv-exten_66 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1752-1775: The macro std-priv-exten_67 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1778-1801: The macro std-priv-exten_68 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1804-1827: The macro std-priv-exten_69 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1830-1853: The macro std-priv-exten_70 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1856-1879: The macro std-priv-exten_71 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1882-1905: The macro std-priv-exten_72 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1908-1931: The macro std-priv-exten_73 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1934-1957: The macro std-priv-exten does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1959-1995: The macro fillcidname does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1997-2015: The macro ciddial does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 2017-2028: The macro ciddial3 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 2030-2048: The macro ciddial2 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 2050-2065: The macro callerid-liar does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 2067-2072: The macro callerid-bad does not end with a return; I will insert one. +LOG: lev:2 file:pbx_ael.c line:129 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:131 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:134 func: pbx_load_module AEL load process: merged config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:137 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. +LOG: lev:4 file:ael2_parse line:531 func: main 172 contexts, 934 extensions, 2482 priorities diff --git a/trunk/pbx/ael/ael-test/ref.ael-test4 b/trunk/pbx/ael/ael-test/ref.ael-test4 new file mode 100644 index 000000000..892b63464 --- /dev/null +++ b/trunk/pbx/ael/ael-test/ref.ael-test4 @@ -0,0 +1,28 @@ + +(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:113 func: pbx_load_module Starting AEL load process. +LOG: lev:2 file:ael.flex line:662 func: setup_filestack --Read in included file ././apptest.ael2, 3474 chars +LOG: lev:3 file:ael.y line:546 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:126 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. +LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 35-35: application call to EndWhile affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 37-37: application call to ExecIf affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 38-38: application call to ExecIfTime affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 44-44: application call to Gosub affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 45-45: application call to GosubIf affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:1346 func: check_goto Warning: file ././apptest.ael2, line 46-46: goto: Couldn't find goto target cont|exten|prior, not even in extensions.conf! +LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 47-47: application call to GotoIf affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 48-48: application call to GotoIfTime affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 58-58: application call to Macro affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2512 func: check_pval_item Warning: file ././apptest.ael2, line 59-59: I am converting the MacroExit call here to a return statement. +LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 60-60: application call to MacroIf affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 85-85: application call to Random affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 94-94: application call to Return affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 119-119: application call to StackPop affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 141-141: application call to While affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:2 file:pbx_ael.c line:129 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:131 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:134 func: pbx_load_module AEL load process: merged config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:137 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. +LOG: lev:4 file:ael2_parse line:531 func: main 1 contexts, 1 extensions, 142 priorities diff --git a/trunk/pbx/ael/ael-test/ref.ael-test5 b/trunk/pbx/ael/ael-test/ref.ael-test5 new file mode 100644 index 000000000..79c327413 --- /dev/null +++ b/trunk/pbx/ael/ael-test/ref.ael-test5 @@ -0,0 +1,14 @@ + +(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:113 func: pbx_load_module Starting AEL load process. +LOG: lev:2 file:pbx_ael.c line:126 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 130-183: The macro stdexten does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 185-192: The macro uvm does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 194-201: The macro bvm does not end with a return; I will insert one. +LOG: lev:2 file:pbx_ael.c line:129 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:131 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:134 func: pbx_load_module AEL load process: merged config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:137 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. +LOG: lev:4 file:ael2_parse line:531 func: main 38 contexts, 91 extensions, 493 priorities diff --git a/trunk/pbx/ael/ael-test/ref.ael-test6 b/trunk/pbx/ael/ael-test/ref.ael-test6 new file mode 100644 index 000000000..968c7c71e --- /dev/null +++ b/trunk/pbx/ael/ael-test/ref.ael-test6 @@ -0,0 +1,24 @@ + +(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:113 func: pbx_load_module Starting AEL load process. +LOG: lev:4 file:ael.flex line:288 func: ael_yylex File=./extensions.ael, line=165, column=49: Mismatched '}' in expression! +LOG: lev:4 file:ael.y line:772 func: ael_yyerror ==== File: ./extensions.ael, Line 165, Cols: 51-51: Error: syntax error, unexpected '=', expecting ')' +LOG: lev:4 file:ael.flex line:288 func: ael_yylex File=./extensions.ael, line=174, column=49: Mismatched '}' in expression! +LOG: lev:4 file:ael.y line:772 func: ael_yyerror ==== File: ./extensions.ael, Line 174, Cols: 51-51: Error: syntax error, unexpected '=', expecting ')' +LOG: lev:4 file:ael.flex line:288 func: ael_yylex File=./extensions.ael, line=222, column=41: Mismatched '}' in expression! +LOG: lev:4 file:ael.y line:772 func: ael_yyerror ==== File: ./extensions.ael, Line 222, Cols: 43-43: Error: syntax error, unexpected '=', expecting ')' +LOG: lev:4 file:ael.y line:772 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:772 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:126 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 116-125: The macro dialout does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 129-182: The macro stdexten does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 184-191: The macro uvm does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 193-200: The macro bvm does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 202-207: The macro checkdnd does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 209-216: The macro checkcf does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 218-230: The macro checkcfb does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 688-694: The macro check-psd-exists does not end with a return; I will insert one. +LOG: lev:4 file:pbx_ael.c line:139 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:531 func: main 0 contexts, 0 extensions, 0 priorities diff --git a/trunk/pbx/ael/ael-test/ref.ael-test7 b/trunk/pbx/ael/ael-test/ref.ael-test7 new file mode 100644 index 000000000..69f0c9f91 --- /dev/null +++ b/trunk/pbx/ael/ael-test/ref.ael-test7 @@ -0,0 +1,19 @@ + +(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:113 func: pbx_load_module Starting AEL load process. +LOG: lev:2 file:pbx_ael.c line:126 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 22-42: The macro stdexten does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 44-49: The macro announce_minutes does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 59-89: The macro checkanddial does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 91-100: The macro trunkdial does not end with a return; I will insert one. +LOG: lev:4 file:pval.c line:2468 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:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 102-112: The macro checklocal does not end with a return; I will insert one. +LOG: lev:4 file:pval.c line:2468 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:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 114-119: The macro autodial does not end with a return; I will insert one. +LOG: lev:4 file:pval.c line:2468 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:pval.c line:2468 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:pval.c line:2426 func: check_pval_item Warning: file ./extensions.ael, line 452-452: macro call to non-existent std-exten-ael! (Not even in the extensions.conf stuff!) +LOG: lev:4 file:pbx_ael.c line:139 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:531 func: main 0 contexts, 0 extensions, 0 priorities diff --git a/trunk/pbx/ael/ael-test/ref.ael-test8 b/trunk/pbx/ael/ael-test/ref.ael-test8 new file mode 100644 index 000000000..4e5d0aa37 --- /dev/null +++ b/trunk/pbx/ael/ael-test/ref.ael-test8 @@ -0,0 +1,11 @@ + +(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:113 func: pbx_load_module Starting AEL load process. +LOG: lev:2 file:pbx_ael.c line:126 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:129 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:131 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:134 func: pbx_load_module AEL load process: merged config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:137 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. +LOG: lev:4 file:ael2_parse line:531 func: main 1 contexts, 7 extensions, 17 priorities diff --git a/trunk/pbx/ael/ael-test/ref.ael-vtest13 b/trunk/pbx/ael/ael-test/ref.ael-vtest13 new file mode 100644 index 000000000..989a18258 --- /dev/null +++ b/trunk/pbx/ael/ael-test/ref.ael-vtest13 @@ -0,0 +1,3030 @@ +[globals] +static=yes +writeprotect=yes +CONSOLE=Console/dsp +IAXINFO=murf:tlhfckoct +FWDNUMBER=544788 +FWDCIDNAME="Joe-Worker" +FWDPASSWORD=zingledoodle +FWDRINGS=Zap/6 +FWDVMBOX=1 + + +[std-exten] +exten => s,1,Set(LOCAL(ext)=${ARG1}) +exten => s,2,Set(LOCAL(dev)=${ARG2}) +exten => s,3,Dial(${dev}/${ext}\,20) +exten => s,4,Goto(sw-1-${DIALSTATUS}\,10) +exten => s,5,NoOp(Finish switch-std-exten-1) +exten => s,6,Return() +exten => a,1,VoiceMailMain(${ext}) +exten => _sw-1-.,10,Voicemail(u${ext}) +exten => _sw-1-.,11,Goto(s\,5) +exten => sw-1-,10,Goto(sw-1-.|10) +exten => sw-1-ANSWER,10,Goto(s\,5) +exten => sw-1-NOANSWER,10,Voicemail(u${ext}) +exten => sw-1-NOANSWER,11,Goto(s\,5) +exten => sw-1-BUSY,10,Voicemail(b${ext}) +exten => sw-1-BUSY,11,Goto(s\,5) + + +[std-priv-exten_1] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-3-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_1-3) +exten => s,11,Return() +exten => _sw-3-.,10,Voicemail(u${ext}) +exten => _sw-3-.,11,Goto(s\,10) +exten => sw-3-,10,Goto(sw-3-.|10) +exten => sw-3-NOANSWER,10,Voicemail(u${ext}) +exten => sw-3-NOANSWER,11,Goto(s\,10) +exten => sw-3-ANSWER,10,Goto(s\,10) +exten => sw-3-BUSY,10,Voicemail(b${ext}) +exten => sw-3-BUSY,11,Goto(s\,10) +exten => sw-3-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-3-DONTCALL,11,Goto(s\,10) +exten => sw-3-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-3-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_2] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-4-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_2-4) +exten => s,11,Return() +exten => _sw-4-.,10,Voicemail(u${ext}) +exten => _sw-4-.,11,Goto(s\,10) +exten => sw-4-,10,Goto(sw-4-.|10) +exten => sw-4-NOANSWER,10,Voicemail(u${ext}) +exten => sw-4-NOANSWER,11,Goto(s\,10) +exten => sw-4-ANSWER,10,Goto(s\,10) +exten => sw-4-BUSY,10,Voicemail(b${ext}) +exten => sw-4-BUSY,11,Goto(s\,10) +exten => sw-4-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-4-DONTCALL,11,Goto(s\,10) +exten => sw-4-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-4-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_3] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-5-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_3-5) +exten => s,11,Return() +exten => _sw-5-.,10,Voicemail(u${ext}) +exten => _sw-5-.,11,Goto(s\,10) +exten => sw-5-,10,Goto(sw-5-.|10) +exten => sw-5-NOANSWER,10,Voicemail(u${ext}) +exten => sw-5-NOANSWER,11,Goto(s\,10) +exten => sw-5-ANSWER,10,Goto(s\,10) +exten => sw-5-BUSY,10,Voicemail(b${ext}) +exten => sw-5-BUSY,11,Goto(s\,10) +exten => sw-5-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-5-DONTCALL,11,Goto(s\,10) +exten => sw-5-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-5-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_4] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-6-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_4-6) +exten => s,11,Return() +exten => _sw-6-.,10,Voicemail(u${ext}) +exten => _sw-6-.,11,Goto(s\,10) +exten => sw-6-,10,Goto(sw-6-.|10) +exten => sw-6-NOANSWER,10,Voicemail(u${ext}) +exten => sw-6-NOANSWER,11,Goto(s\,10) +exten => sw-6-ANSWER,10,Goto(s\,10) +exten => sw-6-BUSY,10,Voicemail(b${ext}) +exten => sw-6-BUSY,11,Goto(s\,10) +exten => sw-6-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-6-DONTCALL,11,Goto(s\,10) +exten => sw-6-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-6-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_5] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-7-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_5-7) +exten => s,11,Return() +exten => _sw-7-.,10,Voicemail(u${ext}) +exten => _sw-7-.,11,Goto(s\,10) +exten => sw-7-,10,Goto(sw-7-.|10) +exten => sw-7-NOANSWER,10,Voicemail(u${ext}) +exten => sw-7-NOANSWER,11,Goto(s\,10) +exten => sw-7-ANSWER,10,Goto(s\,10) +exten => sw-7-BUSY,10,Voicemail(b${ext}) +exten => sw-7-BUSY,11,Goto(s\,10) +exten => sw-7-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-7-DONTCALL,11,Goto(s\,10) +exten => sw-7-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-7-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_6] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-8-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_6-8) +exten => s,11,Return() +exten => _sw-8-.,10,Voicemail(u${ext}) +exten => _sw-8-.,11,Goto(s\,10) +exten => sw-8-,10,Goto(sw-8-.|10) +exten => sw-8-NOANSWER,10,Voicemail(u${ext}) +exten => sw-8-NOANSWER,11,Goto(s\,10) +exten => sw-8-ANSWER,10,Goto(s\,10) +exten => sw-8-BUSY,10,Voicemail(b${ext}) +exten => sw-8-BUSY,11,Goto(s\,10) +exten => sw-8-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-8-DONTCALL,11,Goto(s\,10) +exten => sw-8-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-8-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_7] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-9-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_7-9) +exten => s,11,Return() +exten => _sw-9-.,10,Voicemail(u${ext}) +exten => _sw-9-.,11,Goto(s\,10) +exten => sw-9-,10,Goto(sw-9-.|10) +exten => sw-9-NOANSWER,10,Voicemail(u${ext}) +exten => sw-9-NOANSWER,11,Goto(s\,10) +exten => sw-9-ANSWER,10,Goto(s\,10) +exten => sw-9-BUSY,10,Voicemail(b${ext}) +exten => sw-9-BUSY,11,Goto(s\,10) +exten => sw-9-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-9-DONTCALL,11,Goto(s\,10) +exten => sw-9-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-9-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_8] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-10-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_8-10) +exten => s,11,Return() +exten => _sw-10-.,10,Voicemail(u${ext}) +exten => _sw-10-.,11,Goto(s\,10) +exten => sw-10-,10,Goto(sw-10-.|10) +exten => sw-10-NOANSWER,10,Voicemail(u${ext}) +exten => sw-10-NOANSWER,11,Goto(s\,10) +exten => sw-10-ANSWER,10,Goto(s\,10) +exten => sw-10-BUSY,10,Voicemail(b${ext}) +exten => sw-10-BUSY,11,Goto(s\,10) +exten => sw-10-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-10-DONTCALL,11,Goto(s\,10) +exten => sw-10-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-10-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_9] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-11-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_9-11) +exten => s,11,Return() +exten => _sw-11-.,10,Voicemail(u${ext}) +exten => _sw-11-.,11,Goto(s\,10) +exten => sw-11-,10,Goto(sw-11-.|10) +exten => sw-11-NOANSWER,10,Voicemail(u${ext}) +exten => sw-11-NOANSWER,11,Goto(s\,10) +exten => sw-11-ANSWER,10,Goto(s\,10) +exten => sw-11-BUSY,10,Voicemail(b${ext}) +exten => sw-11-BUSY,11,Goto(s\,10) +exten => sw-11-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-11-DONTCALL,11,Goto(s\,10) +exten => sw-11-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-11-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_10] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-12-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_10-12) +exten => s,11,Return() +exten => _sw-12-.,10,Voicemail(u${ext}) +exten => _sw-12-.,11,Goto(s\,10) +exten => sw-12-,10,Goto(sw-12-.|10) +exten => sw-12-NOANSWER,10,Voicemail(u${ext}) +exten => sw-12-NOANSWER,11,Goto(s\,10) +exten => sw-12-ANSWER,10,Goto(s\,10) +exten => sw-12-BUSY,10,Voicemail(b${ext}) +exten => sw-12-BUSY,11,Goto(s\,10) +exten => sw-12-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-12-DONTCALL,11,Goto(s\,10) +exten => sw-12-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-12-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_11] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-13-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_11-13) +exten => s,11,Return() +exten => _sw-13-.,10,Voicemail(u${ext}) +exten => _sw-13-.,11,Goto(s\,10) +exten => sw-13-,10,Goto(sw-13-.|10) +exten => sw-13-NOANSWER,10,Voicemail(u${ext}) +exten => sw-13-NOANSWER,11,Goto(s\,10) +exten => sw-13-ANSWER,10,Goto(s\,10) +exten => sw-13-BUSY,10,Voicemail(b${ext}) +exten => sw-13-BUSY,11,Goto(s\,10) +exten => sw-13-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-13-DONTCALL,11,Goto(s\,10) +exten => sw-13-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-13-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_12] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-14-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_12-14) +exten => s,11,Return() +exten => _sw-14-.,10,Voicemail(u${ext}) +exten => _sw-14-.,11,Goto(s\,10) +exten => sw-14-,10,Goto(sw-14-.|10) +exten => sw-14-NOANSWER,10,Voicemail(u${ext}) +exten => sw-14-NOANSWER,11,Goto(s\,10) +exten => sw-14-ANSWER,10,Goto(s\,10) +exten => sw-14-BUSY,10,Voicemail(b${ext}) +exten => sw-14-BUSY,11,Goto(s\,10) +exten => sw-14-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-14-DONTCALL,11,Goto(s\,10) +exten => sw-14-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-14-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_13] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-15-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_13-15) +exten => s,11,Return() +exten => _sw-15-.,10,Voicemail(u${ext}) +exten => _sw-15-.,11,Goto(s\,10) +exten => sw-15-,10,Goto(sw-15-.|10) +exten => sw-15-NOANSWER,10,Voicemail(u${ext}) +exten => sw-15-NOANSWER,11,Goto(s\,10) +exten => sw-15-ANSWER,10,Goto(s\,10) +exten => sw-15-BUSY,10,Voicemail(b${ext}) +exten => sw-15-BUSY,11,Goto(s\,10) +exten => sw-15-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-15-DONTCALL,11,Goto(s\,10) +exten => sw-15-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-15-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_14] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-16-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_14-16) +exten => s,11,Return() +exten => _sw-16-.,10,Voicemail(u${ext}) +exten => _sw-16-.,11,Goto(s\,10) +exten => sw-16-,10,Goto(sw-16-.|10) +exten => sw-16-NOANSWER,10,Voicemail(u${ext}) +exten => sw-16-NOANSWER,11,Goto(s\,10) +exten => sw-16-ANSWER,10,Goto(s\,10) +exten => sw-16-BUSY,10,Voicemail(b${ext}) +exten => sw-16-BUSY,11,Goto(s\,10) +exten => sw-16-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-16-DONTCALL,11,Goto(s\,10) +exten => sw-16-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-16-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_15] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-17-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_15-17) +exten => s,11,Return() +exten => _sw-17-.,10,Voicemail(u${ext}) +exten => _sw-17-.,11,Goto(s\,10) +exten => sw-17-,10,Goto(sw-17-.|10) +exten => sw-17-NOANSWER,10,Voicemail(u${ext}) +exten => sw-17-NOANSWER,11,Goto(s\,10) +exten => sw-17-ANSWER,10,Goto(s\,10) +exten => sw-17-BUSY,10,Voicemail(b${ext}) +exten => sw-17-BUSY,11,Goto(s\,10) +exten => sw-17-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-17-DONTCALL,11,Goto(s\,10) +exten => sw-17-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-17-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_16] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-18-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_16-18) +exten => s,11,Return() +exten => _sw-18-.,10,Voicemail(u${ext}) +exten => _sw-18-.,11,Goto(s\,10) +exten => sw-18-,10,Goto(sw-18-.|10) +exten => sw-18-NOANSWER,10,Voicemail(u${ext}) +exten => sw-18-NOANSWER,11,Goto(s\,10) +exten => sw-18-ANSWER,10,Goto(s\,10) +exten => sw-18-BUSY,10,Voicemail(b${ext}) +exten => sw-18-BUSY,11,Goto(s\,10) +exten => sw-18-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-18-DONTCALL,11,Goto(s\,10) +exten => sw-18-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-18-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_17] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-19-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_17-19) +exten => s,11,Return() +exten => _sw-19-.,10,Voicemail(u${ext}) +exten => _sw-19-.,11,Goto(s\,10) +exten => sw-19-,10,Goto(sw-19-.|10) +exten => sw-19-NOANSWER,10,Voicemail(u${ext}) +exten => sw-19-NOANSWER,11,Goto(s\,10) +exten => sw-19-ANSWER,10,Goto(s\,10) +exten => sw-19-BUSY,10,Voicemail(b${ext}) +exten => sw-19-BUSY,11,Goto(s\,10) +exten => sw-19-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-19-DONTCALL,11,Goto(s\,10) +exten => sw-19-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-19-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_18] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-20-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_18-20) +exten => s,11,Return() +exten => _sw-20-.,10,Voicemail(u${ext}) +exten => _sw-20-.,11,Goto(s\,10) +exten => sw-20-,10,Goto(sw-20-.|10) +exten => sw-20-NOANSWER,10,Voicemail(u${ext}) +exten => sw-20-NOANSWER,11,Goto(s\,10) +exten => sw-20-ANSWER,10,Goto(s\,10) +exten => sw-20-BUSY,10,Voicemail(b${ext}) +exten => sw-20-BUSY,11,Goto(s\,10) +exten => sw-20-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-20-DONTCALL,11,Goto(s\,10) +exten => sw-20-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-20-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_19] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-21-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_19-21) +exten => s,11,Return() +exten => _sw-21-.,10,Voicemail(u${ext}) +exten => _sw-21-.,11,Goto(s\,10) +exten => sw-21-,10,Goto(sw-21-.|10) +exten => sw-21-NOANSWER,10,Voicemail(u${ext}) +exten => sw-21-NOANSWER,11,Goto(s\,10) +exten => sw-21-ANSWER,10,Goto(s\,10) +exten => sw-21-BUSY,10,Voicemail(b${ext}) +exten => sw-21-BUSY,11,Goto(s\,10) +exten => sw-21-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-21-DONTCALL,11,Goto(s\,10) +exten => sw-21-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-21-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_20] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-22-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_20-22) +exten => s,11,Return() +exten => _sw-22-.,10,Voicemail(u${ext}) +exten => _sw-22-.,11,Goto(s\,10) +exten => sw-22-,10,Goto(sw-22-.|10) +exten => sw-22-NOANSWER,10,Voicemail(u${ext}) +exten => sw-22-NOANSWER,11,Goto(s\,10) +exten => sw-22-ANSWER,10,Goto(s\,10) +exten => sw-22-BUSY,10,Voicemail(b${ext}) +exten => sw-22-BUSY,11,Goto(s\,10) +exten => sw-22-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-22-DONTCALL,11,Goto(s\,10) +exten => sw-22-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-22-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_21] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-23-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_21-23) +exten => s,11,Return() +exten => _sw-23-.,10,Voicemail(u${ext}) +exten => _sw-23-.,11,Goto(s\,10) +exten => sw-23-,10,Goto(sw-23-.|10) +exten => sw-23-NOANSWER,10,Voicemail(u${ext}) +exten => sw-23-NOANSWER,11,Goto(s\,10) +exten => sw-23-ANSWER,10,Goto(s\,10) +exten => sw-23-BUSY,10,Voicemail(b${ext}) +exten => sw-23-BUSY,11,Goto(s\,10) +exten => sw-23-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-23-DONTCALL,11,Goto(s\,10) +exten => sw-23-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-23-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_22] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-24-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_22-24) +exten => s,11,Return() +exten => _sw-24-.,10,Voicemail(u${ext}) +exten => _sw-24-.,11,Goto(s\,10) +exten => sw-24-,10,Goto(sw-24-.|10) +exten => sw-24-NOANSWER,10,Voicemail(u${ext}) +exten => sw-24-NOANSWER,11,Goto(s\,10) +exten => sw-24-ANSWER,10,Goto(s\,10) +exten => sw-24-BUSY,10,Voicemail(b${ext}) +exten => sw-24-BUSY,11,Goto(s\,10) +exten => sw-24-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-24-DONTCALL,11,Goto(s\,10) +exten => sw-24-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-24-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_23] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-25-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_23-25) +exten => s,11,Return() +exten => _sw-25-.,10,Voicemail(u${ext}) +exten => _sw-25-.,11,Goto(s\,10) +exten => sw-25-,10,Goto(sw-25-.|10) +exten => sw-25-NOANSWER,10,Voicemail(u${ext}) +exten => sw-25-NOANSWER,11,Goto(s\,10) +exten => sw-25-ANSWER,10,Goto(s\,10) +exten => sw-25-BUSY,10,Voicemail(b${ext}) +exten => sw-25-BUSY,11,Goto(s\,10) +exten => sw-25-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-25-DONTCALL,11,Goto(s\,10) +exten => sw-25-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-25-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_24] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-26-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_24-26) +exten => s,11,Return() +exten => _sw-26-.,10,Voicemail(u${ext}) +exten => _sw-26-.,11,Goto(s\,10) +exten => sw-26-,10,Goto(sw-26-.|10) +exten => sw-26-NOANSWER,10,Voicemail(u${ext}) +exten => sw-26-NOANSWER,11,Goto(s\,10) +exten => sw-26-ANSWER,10,Goto(s\,10) +exten => sw-26-BUSY,10,Voicemail(b${ext}) +exten => sw-26-BUSY,11,Goto(s\,10) +exten => sw-26-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-26-DONTCALL,11,Goto(s\,10) +exten => sw-26-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-26-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_25] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-27-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_25-27) +exten => s,11,Return() +exten => _sw-27-.,10,Voicemail(u${ext}) +exten => _sw-27-.,11,Goto(s\,10) +exten => sw-27-,10,Goto(sw-27-.|10) +exten => sw-27-NOANSWER,10,Voicemail(u${ext}) +exten => sw-27-NOANSWER,11,Goto(s\,10) +exten => sw-27-ANSWER,10,Goto(s\,10) +exten => sw-27-BUSY,10,Voicemail(b${ext}) +exten => sw-27-BUSY,11,Goto(s\,10) +exten => sw-27-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-27-DONTCALL,11,Goto(s\,10) +exten => sw-27-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-27-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_26] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-28-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_26-28) +exten => s,11,Return() +exten => _sw-28-.,10,Voicemail(u${ext}) +exten => _sw-28-.,11,Goto(s\,10) +exten => sw-28-,10,Goto(sw-28-.|10) +exten => sw-28-NOANSWER,10,Voicemail(u${ext}) +exten => sw-28-NOANSWER,11,Goto(s\,10) +exten => sw-28-ANSWER,10,Goto(s\,10) +exten => sw-28-BUSY,10,Voicemail(b${ext}) +exten => sw-28-BUSY,11,Goto(s\,10) +exten => sw-28-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-28-DONTCALL,11,Goto(s\,10) +exten => sw-28-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-28-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_27] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-29-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_27-29) +exten => s,11,Return() +exten => _sw-29-.,10,Voicemail(u${ext}) +exten => _sw-29-.,11,Goto(s\,10) +exten => sw-29-,10,Goto(sw-29-.|10) +exten => sw-29-NOANSWER,10,Voicemail(u${ext}) +exten => sw-29-NOANSWER,11,Goto(s\,10) +exten => sw-29-ANSWER,10,Goto(s\,10) +exten => sw-29-BUSY,10,Voicemail(b${ext}) +exten => sw-29-BUSY,11,Goto(s\,10) +exten => sw-29-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-29-DONTCALL,11,Goto(s\,10) +exten => sw-29-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-29-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_28] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-30-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_28-30) +exten => s,11,Return() +exten => _sw-30-.,10,Voicemail(u${ext}) +exten => _sw-30-.,11,Goto(s\,10) +exten => sw-30-,10,Goto(sw-30-.|10) +exten => sw-30-NOANSWER,10,Voicemail(u${ext}) +exten => sw-30-NOANSWER,11,Goto(s\,10) +exten => sw-30-ANSWER,10,Goto(s\,10) +exten => sw-30-BUSY,10,Voicemail(b${ext}) +exten => sw-30-BUSY,11,Goto(s\,10) +exten => sw-30-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-30-DONTCALL,11,Goto(s\,10) +exten => sw-30-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-30-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_29] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-31-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_29-31) +exten => s,11,Return() +exten => _sw-31-.,10,Voicemail(u${ext}) +exten => _sw-31-.,11,Goto(s\,10) +exten => sw-31-,10,Goto(sw-31-.|10) +exten => sw-31-NOANSWER,10,Voicemail(u${ext}) +exten => sw-31-NOANSWER,11,Goto(s\,10) +exten => sw-31-ANSWER,10,Goto(s\,10) +exten => sw-31-BUSY,10,Voicemail(b${ext}) +exten => sw-31-BUSY,11,Goto(s\,10) +exten => sw-31-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-31-DONTCALL,11,Goto(s\,10) +exten => sw-31-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-31-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_30] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-32-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_30-32) +exten => s,11,Return() +exten => _sw-32-.,10,Voicemail(u${ext}) +exten => _sw-32-.,11,Goto(s\,10) +exten => sw-32-,10,Goto(sw-32-.|10) +exten => sw-32-NOANSWER,10,Voicemail(u${ext}) +exten => sw-32-NOANSWER,11,Goto(s\,10) +exten => sw-32-ANSWER,10,Goto(s\,10) +exten => sw-32-BUSY,10,Voicemail(b${ext}) +exten => sw-32-BUSY,11,Goto(s\,10) +exten => sw-32-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-32-DONTCALL,11,Goto(s\,10) +exten => sw-32-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-32-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_31] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-33-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_31-33) +exten => s,11,Return() +exten => _sw-33-.,10,Voicemail(u${ext}) +exten => _sw-33-.,11,Goto(s\,10) +exten => sw-33-,10,Goto(sw-33-.|10) +exten => sw-33-NOANSWER,10,Voicemail(u${ext}) +exten => sw-33-NOANSWER,11,Goto(s\,10) +exten => sw-33-ANSWER,10,Goto(s\,10) +exten => sw-33-BUSY,10,Voicemail(b${ext}) +exten => sw-33-BUSY,11,Goto(s\,10) +exten => sw-33-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-33-DONTCALL,11,Goto(s\,10) +exten => sw-33-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-33-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_32] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-34-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_32-34) +exten => s,11,Return() +exten => _sw-34-.,10,Voicemail(u${ext}) +exten => _sw-34-.,11,Goto(s\,10) +exten => sw-34-,10,Goto(sw-34-.|10) +exten => sw-34-NOANSWER,10,Voicemail(u${ext}) +exten => sw-34-NOANSWER,11,Goto(s\,10) +exten => sw-34-ANSWER,10,Goto(s\,10) +exten => sw-34-BUSY,10,Voicemail(b${ext}) +exten => sw-34-BUSY,11,Goto(s\,10) +exten => sw-34-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-34-DONTCALL,11,Goto(s\,10) +exten => sw-34-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-34-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_33] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-35-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_33-35) +exten => s,11,Return() +exten => _sw-35-.,10,Voicemail(u${ext}) +exten => _sw-35-.,11,Goto(s\,10) +exten => sw-35-,10,Goto(sw-35-.|10) +exten => sw-35-NOANSWER,10,Voicemail(u${ext}) +exten => sw-35-NOANSWER,11,Goto(s\,10) +exten => sw-35-ANSWER,10,Goto(s\,10) +exten => sw-35-BUSY,10,Voicemail(b${ext}) +exten => sw-35-BUSY,11,Goto(s\,10) +exten => sw-35-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-35-DONTCALL,11,Goto(s\,10) +exten => sw-35-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-35-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_34] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-36-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_34-36) +exten => s,11,Return() +exten => _sw-36-.,10,Voicemail(u${ext}) +exten => _sw-36-.,11,Goto(s\,10) +exten => sw-36-,10,Goto(sw-36-.|10) +exten => sw-36-NOANSWER,10,Voicemail(u${ext}) +exten => sw-36-NOANSWER,11,Goto(s\,10) +exten => sw-36-ANSWER,10,Goto(s\,10) +exten => sw-36-BUSY,10,Voicemail(b${ext}) +exten => sw-36-BUSY,11,Goto(s\,10) +exten => sw-36-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-36-DONTCALL,11,Goto(s\,10) +exten => sw-36-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-36-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_35] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-37-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_35-37) +exten => s,11,Return() +exten => _sw-37-.,10,Voicemail(u${ext}) +exten => _sw-37-.,11,Goto(s\,10) +exten => sw-37-,10,Goto(sw-37-.|10) +exten => sw-37-NOANSWER,10,Voicemail(u${ext}) +exten => sw-37-NOANSWER,11,Goto(s\,10) +exten => sw-37-ANSWER,10,Goto(s\,10) +exten => sw-37-BUSY,10,Voicemail(b${ext}) +exten => sw-37-BUSY,11,Goto(s\,10) +exten => sw-37-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-37-DONTCALL,11,Goto(s\,10) +exten => sw-37-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-37-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_36] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-38-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_36-38) +exten => s,11,Return() +exten => _sw-38-.,10,Voicemail(u${ext}) +exten => _sw-38-.,11,Goto(s\,10) +exten => sw-38-,10,Goto(sw-38-.|10) +exten => sw-38-NOANSWER,10,Voicemail(u${ext}) +exten => sw-38-NOANSWER,11,Goto(s\,10) +exten => sw-38-ANSWER,10,Goto(s\,10) +exten => sw-38-BUSY,10,Voicemail(b${ext}) +exten => sw-38-BUSY,11,Goto(s\,10) +exten => sw-38-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-38-DONTCALL,11,Goto(s\,10) +exten => sw-38-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-38-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_37] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-39-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_37-39) +exten => s,11,Return() +exten => _sw-39-.,10,Voicemail(u${ext}) +exten => _sw-39-.,11,Goto(s\,10) +exten => sw-39-,10,Goto(sw-39-.|10) +exten => sw-39-NOANSWER,10,Voicemail(u${ext}) +exten => sw-39-NOANSWER,11,Goto(s\,10) +exten => sw-39-ANSWER,10,Goto(s\,10) +exten => sw-39-BUSY,10,Voicemail(b${ext}) +exten => sw-39-BUSY,11,Goto(s\,10) +exten => sw-39-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-39-DONTCALL,11,Goto(s\,10) +exten => sw-39-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-39-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_38] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-40-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_38-40) +exten => s,11,Return() +exten => _sw-40-.,10,Voicemail(u${ext}) +exten => _sw-40-.,11,Goto(s\,10) +exten => sw-40-,10,Goto(sw-40-.|10) +exten => sw-40-NOANSWER,10,Voicemail(u${ext}) +exten => sw-40-NOANSWER,11,Goto(s\,10) +exten => sw-40-ANSWER,10,Goto(s\,10) +exten => sw-40-BUSY,10,Voicemail(b${ext}) +exten => sw-40-BUSY,11,Goto(s\,10) +exten => sw-40-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-40-DONTCALL,11,Goto(s\,10) +exten => sw-40-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-40-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_39] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-41-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_39-41) +exten => s,11,Return() +exten => _sw-41-.,10,Voicemail(u${ext}) +exten => _sw-41-.,11,Goto(s\,10) +exten => sw-41-,10,Goto(sw-41-.|10) +exten => sw-41-NOANSWER,10,Voicemail(u${ext}) +exten => sw-41-NOANSWER,11,Goto(s\,10) +exten => sw-41-ANSWER,10,Goto(s\,10) +exten => sw-41-BUSY,10,Voicemail(b${ext}) +exten => sw-41-BUSY,11,Goto(s\,10) +exten => sw-41-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-41-DONTCALL,11,Goto(s\,10) +exten => sw-41-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-41-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_40] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-42-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_40-42) +exten => s,11,Return() +exten => _sw-42-.,10,Voicemail(u${ext}) +exten => _sw-42-.,11,Goto(s\,10) +exten => sw-42-,10,Goto(sw-42-.|10) +exten => sw-42-NOANSWER,10,Voicemail(u${ext}) +exten => sw-42-NOANSWER,11,Goto(s\,10) +exten => sw-42-ANSWER,10,Goto(s\,10) +exten => sw-42-BUSY,10,Voicemail(b${ext}) +exten => sw-42-BUSY,11,Goto(s\,10) +exten => sw-42-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-42-DONTCALL,11,Goto(s\,10) +exten => sw-42-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-42-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_41] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-43-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_41-43) +exten => s,11,Return() +exten => _sw-43-.,10,Voicemail(u${ext}) +exten => _sw-43-.,11,Goto(s\,10) +exten => sw-43-,10,Goto(sw-43-.|10) +exten => sw-43-NOANSWER,10,Voicemail(u${ext}) +exten => sw-43-NOANSWER,11,Goto(s\,10) +exten => sw-43-ANSWER,10,Goto(s\,10) +exten => sw-43-BUSY,10,Voicemail(b${ext}) +exten => sw-43-BUSY,11,Goto(s\,10) +exten => sw-43-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-43-DONTCALL,11,Goto(s\,10) +exten => sw-43-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-43-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_42] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-44-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_42-44) +exten => s,11,Return() +exten => _sw-44-.,10,Voicemail(u${ext}) +exten => _sw-44-.,11,Goto(s\,10) +exten => sw-44-,10,Goto(sw-44-.|10) +exten => sw-44-NOANSWER,10,Voicemail(u${ext}) +exten => sw-44-NOANSWER,11,Goto(s\,10) +exten => sw-44-ANSWER,10,Goto(s\,10) +exten => sw-44-BUSY,10,Voicemail(b${ext}) +exten => sw-44-BUSY,11,Goto(s\,10) +exten => sw-44-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-44-DONTCALL,11,Goto(s\,10) +exten => sw-44-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-44-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_43] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-45-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_43-45) +exten => s,11,Return() +exten => _sw-45-.,10,Voicemail(u${ext}) +exten => _sw-45-.,11,Goto(s\,10) +exten => sw-45-,10,Goto(sw-45-.|10) +exten => sw-45-NOANSWER,10,Voicemail(u${ext}) +exten => sw-45-NOANSWER,11,Goto(s\,10) +exten => sw-45-ANSWER,10,Goto(s\,10) +exten => sw-45-BUSY,10,Voicemail(b${ext}) +exten => sw-45-BUSY,11,Goto(s\,10) +exten => sw-45-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-45-DONTCALL,11,Goto(s\,10) +exten => sw-45-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-45-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_44] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-46-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_44-46) +exten => s,11,Return() +exten => _sw-46-.,10,Voicemail(u${ext}) +exten => _sw-46-.,11,Goto(s\,10) +exten => sw-46-,10,Goto(sw-46-.|10) +exten => sw-46-NOANSWER,10,Voicemail(u${ext}) +exten => sw-46-NOANSWER,11,Goto(s\,10) +exten => sw-46-ANSWER,10,Goto(s\,10) +exten => sw-46-BUSY,10,Voicemail(b${ext}) +exten => sw-46-BUSY,11,Goto(s\,10) +exten => sw-46-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-46-DONTCALL,11,Goto(s\,10) +exten => sw-46-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-46-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_45] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-47-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_45-47) +exten => s,11,Return() +exten => _sw-47-.,10,Voicemail(u${ext}) +exten => _sw-47-.,11,Goto(s\,10) +exten => sw-47-,10,Goto(sw-47-.|10) +exten => sw-47-NOANSWER,10,Voicemail(u${ext}) +exten => sw-47-NOANSWER,11,Goto(s\,10) +exten => sw-47-ANSWER,10,Goto(s\,10) +exten => sw-47-BUSY,10,Voicemail(b${ext}) +exten => sw-47-BUSY,11,Goto(s\,10) +exten => sw-47-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-47-DONTCALL,11,Goto(s\,10) +exten => sw-47-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-47-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_46] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-48-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_46-48) +exten => s,11,Return() +exten => _sw-48-.,10,Voicemail(u${ext}) +exten => _sw-48-.,11,Goto(s\,10) +exten => sw-48-,10,Goto(sw-48-.|10) +exten => sw-48-NOANSWER,10,Voicemail(u${ext}) +exten => sw-48-NOANSWER,11,Goto(s\,10) +exten => sw-48-ANSWER,10,Goto(s\,10) +exten => sw-48-BUSY,10,Voicemail(b${ext}) +exten => sw-48-BUSY,11,Goto(s\,10) +exten => sw-48-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-48-DONTCALL,11,Goto(s\,10) +exten => sw-48-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-48-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_47] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-49-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_47-49) +exten => s,11,Return() +exten => _sw-49-.,10,Voicemail(u${ext}) +exten => _sw-49-.,11,Goto(s\,10) +exten => sw-49-,10,Goto(sw-49-.|10) +exten => sw-49-NOANSWER,10,Voicemail(u${ext}) +exten => sw-49-NOANSWER,11,Goto(s\,10) +exten => sw-49-ANSWER,10,Goto(s\,10) +exten => sw-49-BUSY,10,Voicemail(b${ext}) +exten => sw-49-BUSY,11,Goto(s\,10) +exten => sw-49-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-49-DONTCALL,11,Goto(s\,10) +exten => sw-49-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-49-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_48] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-50-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_48-50) +exten => s,11,Return() +exten => _sw-50-.,10,Voicemail(u${ext}) +exten => _sw-50-.,11,Goto(s\,10) +exten => sw-50-,10,Goto(sw-50-.|10) +exten => sw-50-NOANSWER,10,Voicemail(u${ext}) +exten => sw-50-NOANSWER,11,Goto(s\,10) +exten => sw-50-ANSWER,10,Goto(s\,10) +exten => sw-50-BUSY,10,Voicemail(b${ext}) +exten => sw-50-BUSY,11,Goto(s\,10) +exten => sw-50-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-50-DONTCALL,11,Goto(s\,10) +exten => sw-50-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-50-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_49] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-51-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_49-51) +exten => s,11,Return() +exten => _sw-51-.,10,Voicemail(u${ext}) +exten => _sw-51-.,11,Goto(s\,10) +exten => sw-51-,10,Goto(sw-51-.|10) +exten => sw-51-NOANSWER,10,Voicemail(u${ext}) +exten => sw-51-NOANSWER,11,Goto(s\,10) +exten => sw-51-ANSWER,10,Goto(s\,10) +exten => sw-51-BUSY,10,Voicemail(b${ext}) +exten => sw-51-BUSY,11,Goto(s\,10) +exten => sw-51-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-51-DONTCALL,11,Goto(s\,10) +exten => sw-51-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-51-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_50] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-52-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_50-52) +exten => s,11,Return() +exten => _sw-52-.,10,Voicemail(u${ext}) +exten => _sw-52-.,11,Goto(s\,10) +exten => sw-52-,10,Goto(sw-52-.|10) +exten => sw-52-NOANSWER,10,Voicemail(u${ext}) +exten => sw-52-NOANSWER,11,Goto(s\,10) +exten => sw-52-ANSWER,10,Goto(s\,10) +exten => sw-52-BUSY,10,Voicemail(b${ext}) +exten => sw-52-BUSY,11,Goto(s\,10) +exten => sw-52-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-52-DONTCALL,11,Goto(s\,10) +exten => sw-52-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-52-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_51] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-53-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_51-53) +exten => s,11,Return() +exten => _sw-53-.,10,Voicemail(u${ext}) +exten => _sw-53-.,11,Goto(s\,10) +exten => sw-53-,10,Goto(sw-53-.|10) +exten => sw-53-NOANSWER,10,Voicemail(u${ext}) +exten => sw-53-NOANSWER,11,Goto(s\,10) +exten => sw-53-ANSWER,10,Goto(s\,10) +exten => sw-53-BUSY,10,Voicemail(b${ext}) +exten => sw-53-BUSY,11,Goto(s\,10) +exten => sw-53-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-53-DONTCALL,11,Goto(s\,10) +exten => sw-53-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-53-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_52] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-54-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_52-54) +exten => s,11,Return() +exten => _sw-54-.,10,Voicemail(u${ext}) +exten => _sw-54-.,11,Goto(s\,10) +exten => sw-54-,10,Goto(sw-54-.|10) +exten => sw-54-NOANSWER,10,Voicemail(u${ext}) +exten => sw-54-NOANSWER,11,Goto(s\,10) +exten => sw-54-ANSWER,10,Goto(s\,10) +exten => sw-54-BUSY,10,Voicemail(b${ext}) +exten => sw-54-BUSY,11,Goto(s\,10) +exten => sw-54-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-54-DONTCALL,11,Goto(s\,10) +exten => sw-54-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-54-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_53] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-55-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_53-55) +exten => s,11,Return() +exten => _sw-55-.,10,Voicemail(u${ext}) +exten => _sw-55-.,11,Goto(s\,10) +exten => sw-55-,10,Goto(sw-55-.|10) +exten => sw-55-NOANSWER,10,Voicemail(u${ext}) +exten => sw-55-NOANSWER,11,Goto(s\,10) +exten => sw-55-ANSWER,10,Goto(s\,10) +exten => sw-55-BUSY,10,Voicemail(b${ext}) +exten => sw-55-BUSY,11,Goto(s\,10) +exten => sw-55-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-55-DONTCALL,11,Goto(s\,10) +exten => sw-55-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-55-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_54] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-56-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_54-56) +exten => s,11,Return() +exten => _sw-56-.,10,Voicemail(u${ext}) +exten => _sw-56-.,11,Goto(s\,10) +exten => sw-56-,10,Goto(sw-56-.|10) +exten => sw-56-NOANSWER,10,Voicemail(u${ext}) +exten => sw-56-NOANSWER,11,Goto(s\,10) +exten => sw-56-ANSWER,10,Goto(s\,10) +exten => sw-56-BUSY,10,Voicemail(b${ext}) +exten => sw-56-BUSY,11,Goto(s\,10) +exten => sw-56-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-56-DONTCALL,11,Goto(s\,10) +exten => sw-56-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-56-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_55] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-57-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_55-57) +exten => s,11,Return() +exten => _sw-57-.,10,Voicemail(u${ext}) +exten => _sw-57-.,11,Goto(s\,10) +exten => sw-57-,10,Goto(sw-57-.|10) +exten => sw-57-NOANSWER,10,Voicemail(u${ext}) +exten => sw-57-NOANSWER,11,Goto(s\,10) +exten => sw-57-ANSWER,10,Goto(s\,10) +exten => sw-57-BUSY,10,Voicemail(b${ext}) +exten => sw-57-BUSY,11,Goto(s\,10) +exten => sw-57-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-57-DONTCALL,11,Goto(s\,10) +exten => sw-57-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-57-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_56] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-58-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_56-58) +exten => s,11,Return() +exten => _sw-58-.,10,Voicemail(u${ext}) +exten => _sw-58-.,11,Goto(s\,10) +exten => sw-58-,10,Goto(sw-58-.|10) +exten => sw-58-NOANSWER,10,Voicemail(u${ext}) +exten => sw-58-NOANSWER,11,Goto(s\,10) +exten => sw-58-ANSWER,10,Goto(s\,10) +exten => sw-58-BUSY,10,Voicemail(b${ext}) +exten => sw-58-BUSY,11,Goto(s\,10) +exten => sw-58-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-58-DONTCALL,11,Goto(s\,10) +exten => sw-58-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-58-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_57] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-59-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_57-59) +exten => s,11,Return() +exten => _sw-59-.,10,Voicemail(u${ext}) +exten => _sw-59-.,11,Goto(s\,10) +exten => sw-59-,10,Goto(sw-59-.|10) +exten => sw-59-NOANSWER,10,Voicemail(u${ext}) +exten => sw-59-NOANSWER,11,Goto(s\,10) +exten => sw-59-ANSWER,10,Goto(s\,10) +exten => sw-59-BUSY,10,Voicemail(b${ext}) +exten => sw-59-BUSY,11,Goto(s\,10) +exten => sw-59-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-59-DONTCALL,11,Goto(s\,10) +exten => sw-59-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-59-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_58] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-60-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_58-60) +exten => s,11,Return() +exten => _sw-60-.,10,Voicemail(u${ext}) +exten => _sw-60-.,11,Goto(s\,10) +exten => sw-60-,10,Goto(sw-60-.|10) +exten => sw-60-NOANSWER,10,Voicemail(u${ext}) +exten => sw-60-NOANSWER,11,Goto(s\,10) +exten => sw-60-ANSWER,10,Goto(s\,10) +exten => sw-60-BUSY,10,Voicemail(b${ext}) +exten => sw-60-BUSY,11,Goto(s\,10) +exten => sw-60-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-60-DONTCALL,11,Goto(s\,10) +exten => sw-60-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-60-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_59] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-61-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_59-61) +exten => s,11,Return() +exten => _sw-61-.,10,Voicemail(u${ext}) +exten => _sw-61-.,11,Goto(s\,10) +exten => sw-61-,10,Goto(sw-61-.|10) +exten => sw-61-NOANSWER,10,Voicemail(u${ext}) +exten => sw-61-NOANSWER,11,Goto(s\,10) +exten => sw-61-ANSWER,10,Goto(s\,10) +exten => sw-61-BUSY,10,Voicemail(b${ext}) +exten => sw-61-BUSY,11,Goto(s\,10) +exten => sw-61-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-61-DONTCALL,11,Goto(s\,10) +exten => sw-61-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-61-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_60] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-62-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_60-62) +exten => s,11,Return() +exten => _sw-62-.,10,Voicemail(u${ext}) +exten => _sw-62-.,11,Goto(s\,10) +exten => sw-62-,10,Goto(sw-62-.|10) +exten => sw-62-NOANSWER,10,Voicemail(u${ext}) +exten => sw-62-NOANSWER,11,Goto(s\,10) +exten => sw-62-ANSWER,10,Goto(s\,10) +exten => sw-62-BUSY,10,Voicemail(b${ext}) +exten => sw-62-BUSY,11,Goto(s\,10) +exten => sw-62-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-62-DONTCALL,11,Goto(s\,10) +exten => sw-62-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-62-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_61] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-63-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_61-63) +exten => s,11,Return() +exten => _sw-63-.,10,Voicemail(u${ext}) +exten => _sw-63-.,11,Goto(s\,10) +exten => sw-63-,10,Goto(sw-63-.|10) +exten => sw-63-NOANSWER,10,Voicemail(u${ext}) +exten => sw-63-NOANSWER,11,Goto(s\,10) +exten => sw-63-ANSWER,10,Goto(s\,10) +exten => sw-63-BUSY,10,Voicemail(b${ext}) +exten => sw-63-BUSY,11,Goto(s\,10) +exten => sw-63-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-63-DONTCALL,11,Goto(s\,10) +exten => sw-63-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-63-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_62] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-64-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_62-64) +exten => s,11,Return() +exten => _sw-64-.,10,Voicemail(u${ext}) +exten => _sw-64-.,11,Goto(s\,10) +exten => sw-64-,10,Goto(sw-64-.|10) +exten => sw-64-NOANSWER,10,Voicemail(u${ext}) +exten => sw-64-NOANSWER,11,Goto(s\,10) +exten => sw-64-ANSWER,10,Goto(s\,10) +exten => sw-64-BUSY,10,Voicemail(b${ext}) +exten => sw-64-BUSY,11,Goto(s\,10) +exten => sw-64-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-64-DONTCALL,11,Goto(s\,10) +exten => sw-64-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-64-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_63] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-65-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_63-65) +exten => s,11,Return() +exten => _sw-65-.,10,Voicemail(u${ext}) +exten => _sw-65-.,11,Goto(s\,10) +exten => sw-65-,10,Goto(sw-65-.|10) +exten => sw-65-NOANSWER,10,Voicemail(u${ext}) +exten => sw-65-NOANSWER,11,Goto(s\,10) +exten => sw-65-ANSWER,10,Goto(s\,10) +exten => sw-65-BUSY,10,Voicemail(b${ext}) +exten => sw-65-BUSY,11,Goto(s\,10) +exten => sw-65-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-65-DONTCALL,11,Goto(s\,10) +exten => sw-65-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-65-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_64] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-66-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_64-66) +exten => s,11,Return() +exten => _sw-66-.,10,Voicemail(u${ext}) +exten => _sw-66-.,11,Goto(s\,10) +exten => sw-66-,10,Goto(sw-66-.|10) +exten => sw-66-NOANSWER,10,Voicemail(u${ext}) +exten => sw-66-NOANSWER,11,Goto(s\,10) +exten => sw-66-ANSWER,10,Goto(s\,10) +exten => sw-66-BUSY,10,Voicemail(b${ext}) +exten => sw-66-BUSY,11,Goto(s\,10) +exten => sw-66-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-66-DONTCALL,11,Goto(s\,10) +exten => sw-66-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-66-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_65] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-67-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_65-67) +exten => s,11,Return() +exten => _sw-67-.,10,Voicemail(u${ext}) +exten => _sw-67-.,11,Goto(s\,10) +exten => sw-67-,10,Goto(sw-67-.|10) +exten => sw-67-NOANSWER,10,Voicemail(u${ext}) +exten => sw-67-NOANSWER,11,Goto(s\,10) +exten => sw-67-ANSWER,10,Goto(s\,10) +exten => sw-67-BUSY,10,Voicemail(b${ext}) +exten => sw-67-BUSY,11,Goto(s\,10) +exten => sw-67-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-67-DONTCALL,11,Goto(s\,10) +exten => sw-67-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-67-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_66] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-68-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_66-68) +exten => s,11,Return() +exten => _sw-68-.,10,Voicemail(u${ext}) +exten => _sw-68-.,11,Goto(s\,10) +exten => sw-68-,10,Goto(sw-68-.|10) +exten => sw-68-NOANSWER,10,Voicemail(u${ext}) +exten => sw-68-NOANSWER,11,Goto(s\,10) +exten => sw-68-ANSWER,10,Goto(s\,10) +exten => sw-68-BUSY,10,Voicemail(b${ext}) +exten => sw-68-BUSY,11,Goto(s\,10) +exten => sw-68-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-68-DONTCALL,11,Goto(s\,10) +exten => sw-68-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-68-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_67] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-69-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_67-69) +exten => s,11,Return() +exten => _sw-69-.,10,Voicemail(u${ext}) +exten => _sw-69-.,11,Goto(s\,10) +exten => sw-69-,10,Goto(sw-69-.|10) +exten => sw-69-NOANSWER,10,Voicemail(u${ext}) +exten => sw-69-NOANSWER,11,Goto(s\,10) +exten => sw-69-ANSWER,10,Goto(s\,10) +exten => sw-69-BUSY,10,Voicemail(b${ext}) +exten => sw-69-BUSY,11,Goto(s\,10) +exten => sw-69-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-69-DONTCALL,11,Goto(s\,10) +exten => sw-69-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-69-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_68] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-70-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_68-70) +exten => s,11,Return() +exten => _sw-70-.,10,Voicemail(u${ext}) +exten => _sw-70-.,11,Goto(s\,10) +exten => sw-70-,10,Goto(sw-70-.|10) +exten => sw-70-NOANSWER,10,Voicemail(u${ext}) +exten => sw-70-NOANSWER,11,Goto(s\,10) +exten => sw-70-ANSWER,10,Goto(s\,10) +exten => sw-70-BUSY,10,Voicemail(b${ext}) +exten => sw-70-BUSY,11,Goto(s\,10) +exten => sw-70-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-70-DONTCALL,11,Goto(s\,10) +exten => sw-70-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-70-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_69] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-71-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_69-71) +exten => s,11,Return() +exten => _sw-71-.,10,Voicemail(u${ext}) +exten => _sw-71-.,11,Goto(s\,10) +exten => sw-71-,10,Goto(sw-71-.|10) +exten => sw-71-NOANSWER,10,Voicemail(u${ext}) +exten => sw-71-NOANSWER,11,Goto(s\,10) +exten => sw-71-ANSWER,10,Goto(s\,10) +exten => sw-71-BUSY,10,Voicemail(b${ext}) +exten => sw-71-BUSY,11,Goto(s\,10) +exten => sw-71-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-71-DONTCALL,11,Goto(s\,10) +exten => sw-71-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-71-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_70] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-72-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_70-72) +exten => s,11,Return() +exten => _sw-72-.,10,Voicemail(u${ext}) +exten => _sw-72-.,11,Goto(s\,10) +exten => sw-72-,10,Goto(sw-72-.|10) +exten => sw-72-NOANSWER,10,Voicemail(u${ext}) +exten => sw-72-NOANSWER,11,Goto(s\,10) +exten => sw-72-ANSWER,10,Goto(s\,10) +exten => sw-72-BUSY,10,Voicemail(b${ext}) +exten => sw-72-BUSY,11,Goto(s\,10) +exten => sw-72-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-72-DONTCALL,11,Goto(s\,10) +exten => sw-72-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-72-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_71] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-73-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_71-73) +exten => s,11,Return() +exten => _sw-73-.,10,Voicemail(u${ext}) +exten => _sw-73-.,11,Goto(s\,10) +exten => sw-73-,10,Goto(sw-73-.|10) +exten => sw-73-NOANSWER,10,Voicemail(u${ext}) +exten => sw-73-NOANSWER,11,Goto(s\,10) +exten => sw-73-ANSWER,10,Goto(s\,10) +exten => sw-73-BUSY,10,Voicemail(b${ext}) +exten => sw-73-BUSY,11,Goto(s\,10) +exten => sw-73-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-73-DONTCALL,11,Goto(s\,10) +exten => sw-73-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-73-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_72] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-74-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_72-74) +exten => s,11,Return() +exten => _sw-74-.,10,Voicemail(u${ext}) +exten => _sw-74-.,11,Goto(s\,10) +exten => sw-74-,10,Goto(sw-74-.|10) +exten => sw-74-NOANSWER,10,Voicemail(u${ext}) +exten => sw-74-NOANSWER,11,Goto(s\,10) +exten => sw-74-ANSWER,10,Goto(s\,10) +exten => sw-74-BUSY,10,Voicemail(b${ext}) +exten => sw-74-BUSY,11,Goto(s\,10) +exten => sw-74-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-74-DONTCALL,11,Goto(s\,10) +exten => sw-74-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-74-TORTURE,11,Goto(s\,10) + + +[std-priv-exten_73] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-75-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten_73-75) +exten => s,11,Return() +exten => _sw-75-.,10,Voicemail(u${ext}) +exten => _sw-75-.,11,Goto(s\,10) +exten => sw-75-,10,Goto(sw-75-.|10) +exten => sw-75-NOANSWER,10,Voicemail(u${ext}) +exten => sw-75-NOANSWER,11,Goto(s\,10) +exten => sw-75-ANSWER,10,Goto(s\,10) +exten => sw-75-BUSY,10,Voicemail(b${ext}) +exten => sw-75-BUSY,11,Goto(s\,10) +exten => sw-75-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-75-DONTCALL,11,Goto(s\,10) +exten => sw-75-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-75-TORTURE,11,Goto(s\,10) + + +[std-priv-exten] +exten => s,1,Set(LOCAL(dev)=${ARG1}) +exten => s,2,Set(LOCAL(ext)=${ARG2}) +exten => s,3,Set(LOCAL(timeout)=${ARG3}) +exten => s,4,Set(LOCAL(opts)=${ARG4}) +exten => s,5,Set(LOCAL(torcont)=${ARG5}) +exten => s,6,Set(LOCAL(dontcont)=${ARG6}) +exten => s,7,Dial(${dev}\,${timeout}\,${opts}) +exten => s,8,NoOp(${DIALSTATUS} was chosen) +exten => s,9,Goto(sw-76-${DIALSTATUS}\,10) +exten => s,10,NoOp(Finish switch-std-priv-exten-76) +exten => s,11,Return() +exten => _sw-76-.,10,Voicemail(u${ext}) +exten => _sw-76-.,11,Goto(s\,10) +exten => sw-76-,10,Goto(sw-76-.|10) +exten => sw-76-NOANSWER,10,Voicemail(u${ext}) +exten => sw-76-NOANSWER,11,Goto(s\,10) +exten => sw-76-ANSWER,10,Goto(s\,10) +exten => sw-76-BUSY,10,Voicemail(b${ext}) +exten => sw-76-BUSY,11,Goto(s\,10) +exten => sw-76-DONTCALL,10,Goto(${dontcont}\,s\,begin) +exten => sw-76-DONTCALL,11,Goto(s\,10) +exten => sw-76-TORTURE,10,Goto(${torcont}\,s\,begin) +exten => sw-76-TORTURE,11,Goto(s\,10) + + +[fillcidname] +exten => s,1,GotoIf($["${CALLERID(num)}" = "" ]?2:3) +exten => s,2,Return() +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(num)})=${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,Return() + + +[ciddial] +exten => s,1,Set(LOCAL(dialnum)=${ARG1}) +exten => s,2,Set(LOCAL(lookup)=${ARG2}) +exten => s,3,Set(LOCAL(waittime)=${ARG3}) +exten => s,4,Set(LOCAL(dialopts)=${ARG4}) +exten => s,5,Set(LOCAL(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) +exten => s,20,Return() + + +[ciddial3] +exten => s,1,Set(LOCAL(dialnum)=${ARG1}) +exten => s,2,Set(LOCAL(lookup)=${ARG2}) +exten => s,3,Set(LOCAL(waittime)=${ARG3}) +exten => s,4,Set(LOCAL(dialopts)=${ARG4}) +exten => s,5,Set(LOCAL(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) +exten => s,14,Return() + + +[ciddial2] +exten => s,1,Set(LOCAL(dialnum)=${ARG1}) +exten => s,2,Set(LOCAL(lookup)=${ARG2}) +exten => s,3,Set(LOCAL(waittime)=${ARG3}) +exten => s,4,Set(LOCAL(dialopts)=${ARG4}) +exten => s,5,Set(LOCAL(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) +exten => s,20,Return() + + +[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() +exten => s,4,Return() + + +[callerid-bad] +exten => s,1,Set(mycid=$[${CALLERID(num)}:"1([0-9]+)"]) +exten => s,2,Set(CALLERID(num)=${mycid}) +exten => s,3,Wait(0) +exten => s,4,Return() + + +[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,Gosub(std-priv-exten\,s\,1(Zap/3r1&Zap/5r1\,2\,25\,mtw\,telemarket\,telemarket)) +exten => s,8,Hangup() +exten => s,9,Return() +exten => s,10,NoOp(Finish if-homeline-87) +exten => s,11(postPriv),Gosub(fillcidname\,s\,1) +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,Gosub(callerid-bad\,s\,1) +exten => s,18,NoOp(Finish if-homeline-88) +exten => s,19,GotoIf($["${CALLERID(num)}" = "7077577685" & "${CALLERID(name)}" : "Privacy Manager" ]?20:21) +exten => s,20,Gosub(callerid-liar\,s\,1) +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:36) +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,Goto(sw-92-${direct}\,10) +exten => s,35,NoOp(Finish switch-if-homeline-91-92) +exten => s,36,NoOp(Finish if-homeline-91) +exten => s,37(loopback),GotoIfTime(*\,*\,20-25\,dec?39) +exten => s,38,Goto(41) +exten => s,39,Playback(greetings/christmas) +exten => s,40,Goto(102) +exten => s,41,GotoIfTime(*\,*\,31\,dec?43) +exten => s,42,Goto(45) +exten => s,43,Playback(greetings/newyear) +exten => s,44,Goto(101) +exten => s,45,GotoIfTime(*\,*\,1\,jan?47) +exten => s,46,Goto(49) +exten => s,47,Playback(greetings/newyear) +exten => s,48,Goto(100) +exten => s,49,GotoIfTime(*\,*\,14\,feb?51) +exten => s,50,Goto(53) +exten => s,51,Playback(greetings/valentines) +exten => s,52,Goto(99) +exten => s,53,GotoIfTime(*\,*\,17\,mar?55) +exten => s,54,Goto(57) +exten => s,55,Playback(greetings/stPat) +exten => s,56,Goto(98) +exten => s,57,GotoIfTime(*\,*\,31\,oct?59) +exten => s,58,Goto(61) +exten => s,59,Playback(greetings/halloween) +exten => s,60,Goto(97) +exten => s,61,GotoIfTime(*\,mon\,15-21\,jan?63) +exten => s,62,Goto(65) +exten => s,63,Playback(greetings/mlkDay) +exten => s,64,Goto(96) +exten => s,65,GotoIfTime(*\,thu\,22-28\,nov?67) +exten => s,66,Goto(69) +exten => s,67,Playback(greetings/thanksgiving) +exten => s,68,Goto(95) +exten => s,69,GotoIfTime(*\,mon\,25-31\,may?71) +exten => s,70,Goto(73) +exten => s,71,Playback(greetings/memorial) +exten => s,72,Goto(94) +exten => s,73,GotoIfTime(*\,mon\,1-7\,sep?75) +exten => s,74,Goto(77) +exten => s,75,Playback(greetings/labor) +exten => s,76,Goto(93) +exten => s,77,GotoIfTime(*\,mon\,15-21\,feb?79) +exten => s,78,Goto(81) +exten => s,79,Playback(greetings/president) +exten => s,80,Goto(92) +exten => s,81,GotoIfTime(*\,sun\,8-14\,may?83) +exten => s,82,Goto(85) +exten => s,83,Playback(greetings/mothers) +exten => s,84,Goto(91) +exten => s,85,GotoIfTime(*\,sun\,15-21\,jun?87) +exten => s,86,Goto(89) +exten => s,87,Playback(greetings/fathers) +exten => s,88,Goto(90) +exten => s,89,Playback(greetings/hello) +exten => s,90,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,91,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,92,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,93,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,94,NoOp(Finish iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-homeline-93-94-95-96-97-98-99-100-101) +exten => s,95,NoOp(Finish iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-homeline-93-94-95-96-97-98-99-100) +exten => s,96,NoOp(Finish iftime-iftime-iftime-iftime-iftime-iftime-iftime-homeline-93-94-95-96-97-98-99) +exten => s,97,NoOp(Finish iftime-iftime-iftime-iftime-iftime-iftime-homeline-93-94-95-96-97-98) +exten => s,98,NoOp(Finish iftime-iftime-iftime-iftime-iftime-homeline-93-94-95-96-97) +exten => s,99,NoOp(Finish iftime-iftime-iftime-iftime-homeline-93-94-95-96) +exten => s,100,NoOp(Finish iftime-iftime-iftime-homeline-93-94-95) +exten => s,101,NoOp(Finish iftime-iftime-homeline-93-94) +exten => s,102,NoOp(Finish iftime-homeline-93) +exten => s,103,Background(murphy-homeline-intro1) +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,Gosub(std-priv-exten\,s\,1(Zap/3r1&Zap/5r1\,2\,25\,mtw\,telemarket\,telemarket)) +exten => sw-92-2,11,Goto(s\,loopback) +exten => sw-92-1,10,Gosub(std-priv-exten\,s\,1(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,Gosub(std-priv-exten\,s\,1(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,Gosub(std-priv-exten\,s\,1(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,Gosub(std-priv-exten\,s\,1(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,Gosub(std-priv-exten\,s\,1(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,Gosub(std-priv-exten\,s\,1(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,Gosub(std-priv-exten\,s\,1(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,Gosub(std-priv-exten\,s\,1(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,Gosub(std-priv-exten\,s\,1(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,Gosub(fillcidname\,s\,1) +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,Gosub(std-priv-exten\,s\,1(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,Gosub(ciddial\,s\,1(${EXTEN:1}\,${EXTEN:2}\,30\,TW\,Zap/1)) +exten => _9754XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:1}\,707${EXTEN:1}\,30\,TW\,Zap/1)) +exten => _9574XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:1}\,707${EXTEN:1}\,30\,TW\,Zap/1)) +exten => _9202XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:1}\,707${EXTEN:1}\,30\,TW\,Zap/1)) +exten => _9219XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:1}\,707${EXTEN:1}\,30\,TW\,Zap/1)) +exten => _9254XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:1}\,707${EXTEN:1}\,30\,TW\,Zap/1)) +exten => _9716XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:1}\,707${EXTEN:1}\,30\,TW\,Zap/1)) +exten => _9NXXXXXX,1,Gosub(ciddial\,s\,1(1707${EXTEN:1}\,707${EXTEN:1}\,30\,TW\,Zap/1)) +exten => _9011.,1,Gosub(ciddial\,s\,1(${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,Gosub(ciddial2\,s\,1(${EXTEN:1}\,${EXTEN:2}\,30\,TW\,Zap/1)) +exten => _9754XXXX,1,Gosub(ciddial2\,s\,1(${EXTEN:1}\,707${EXTEN:1}\,30\,TW\,Zap/1)) +exten => _9574XXXX,1,Gosub(ciddial2\,s\,1(${EXTEN:1}\,707${EXTEN:1}\,30\,TW\,Zap/1)) +exten => _9202XXXX,1,Gosub(ciddial2\,s\,1(${EXTEN:1}\,707${EXTEN:1}\,30\,TW\,Zap/1)) +exten => _9219XXXX,1,Gosub(ciddial2\,s\,1(${EXTEN:1}\,707${EXTEN:1}\,30\,TW\,Zap/1)) +exten => _9254XXXX,1,Gosub(ciddial2\,s\,1(${EXTEN:1}\,707${EXTEN:1}\,30\,TW\,Zap/1)) +exten => _9716XXXX,1,Gosub(ciddial2\,s\,1(${EXTEN:1}\,707${EXTEN:1}\,30\,TW\,Zap/1)) +exten => _9NXXXXXX,1,Gosub(ciddial2\,s\,1(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,Gosub(ciddial\,s\,1(${EXTEN:1}#\,${EXTEN:2}\,30\,TW\,Zap/2)) +exten => _8754XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:1}#\,707${EXTEN:1}\,30\,TW\,Zap/2)) +exten => _8574XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:1}#\,707${EXTEN:1}\,30\,TW\,Zap/2)) +exten => _8202XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:1}#\,707${EXTEN:1}\,30\,TW\,Zap/2)) +exten => _8219XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:1}#\,707${EXTEN:1}\,30\,TW\,Zap/2)) +exten => _8254XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:1}#\,707${EXTEN:1}\,30\,TW\,Zap/2)) +exten => _8716XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:1}#\,707${EXTEN:1}\,30\,TW\,Zap/2)) +exten => _8NXXXXXX,1,Gosub(ciddial\,s\,1(${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,Gosub(ciddial3\,s\,1(${EXTEN:1}#\,${EXTEN:2}\,30\,TW\,Zap/1)) +exten => _8754XXXX,1,Gosub(ciddial3\,s\,1(${EXTEN:1}#\,707${EXTEN:1}\,30\,TW\,Zap/1)) +exten => _8574XXXX,1,Gosub(ciddial3\,s\,1(${EXTEN:1}#\,707${EXTEN:1}\,30\,TW\,Zap/1)) +exten => _8202XXXX,1,Gosub(ciddial3\,s\,1(${EXTEN:1}#\,707${EXTEN:1}\,30\,TW\,Zap/1)) +exten => _8219XXXX,1,Gosub(ciddial3\,s\,1(${EXTEN:1}#\,707${EXTEN:1}\,30\,TW\,Zap/1)) +exten => _8254XXXX,1,Gosub(ciddial3\,s\,1(${EXTEN:1}#\,707${EXTEN:1}\,30\,TW\,Zap/1)) +exten => _8716XXXX,1,Gosub(ciddial3\,s\,1(${EXTEN:1}#\,707${EXTEN:1}\,30\,TW\,Zap/1)) +exten => _8NXXXXXX,1,Gosub(ciddial3\,s\,1(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,Gosub(std-priv-exten\,s\,1(Zap/3&Zap/5\,2\,35\,mtw\,telemarket\,telemarket)) +exten => 1,2,Goto(s\,loopback) +exten => 2,1,Gosub(std-priv-exten\,s\,1(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,Gosub(ciddial\,s\,1(1${EXTEN:3}\,${EXTEN}\,30\,TW\,Zap/1)) +exten => _707219XXXX,1,Gosub(ciddial\,s\,1(1${EXTEN:3}\,${EXTEN}\,30\,TW\,Zap/1)) +exten => _707254XXXX,1,Gosub(ciddial\,s\,1(1${EXTEN:3}\,${EXTEN}\,30\,TW\,Zap/1)) +exten => _707716XXXX,1,Gosub(ciddial\,s\,1(1${EXTEN:3}\,${EXTEN}\,30\,TW\,Zap/1)) +exten => _707754XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:3}\,${EXTEN}\,30\,TW\,Zap/1)) +exten => _707574XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:3}\,${EXTEN}\,30\,TW\,Zap/1)) +exten => _NXXNXXXXXX,1,Gosub(ciddial\,s\,1(1${EXTEN}\,${EXTEN}\,30\,TW\,Zap/1)) +exten => _1NXXNXXXXXX,1,Gosub(ciddial\,s\,1(${EXTEN}\,${EXTEN:1}\,30\,TW\,Zap/1)) +exten => _754XXXX,1,Gosub(ciddial\,s\,1(${EXTEN}\,707${EXTEN}\,30\,TW\,Zap/1)) +exten => _574XXXX,1,Gosub(ciddial\,s\,1(${EXTEN}\,707${EXTEN}\,30\,TW\,Zap/1)) +exten => _NXXXXXX,1,Gosub(ciddial\,s\,1(1707${EXTEN}\,707${EXTEN}\,30\,TW\,Zap/1)) +exten => _911,1,Gosub(ciddial\,s\,1(911\,911\,30\,TW\,Zap/1)) +exten => _411,1,Gosub(ciddial\,s\,1(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,Gosub(ciddial\,s\,1(1${EXTEN:3}\,${EXTEN}\,30\,TW\,Zap/1)) +exten => _707219XXXX,1,Gosub(ciddial\,s\,1(1${EXTEN:3}\,${EXTEN}\,30\,TW\,Zap/1)) +exten => _707254XXXX,1,Gosub(ciddial\,s\,1(1${EXTEN:3}\,${EXTEN}\,30\,TW\,Zap/1)) +exten => _707716XXXX,1,Gosub(ciddial\,s\,1(1${EXTEN:3}\,${EXTEN}\,30\,TW\,Zap/1)) +exten => _707754XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:3}\,${EXTEN}\,30\,TW\,Zap/1)) +exten => _707574XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:3}\,${EXTEN}\,30\,TW\,Zap/1)) +exten => _NXXNXXXXXX,1,Gosub(ciddial\,s\,1(1${EXTEN}\,${EXTEN}\,30\,TW\,Zap/1)) +exten => _1NXXNXXXXXX,1,Gosub(ciddial\,s\,1(${EXTEN}\,${EXTEN:1}\,30\,TW\,Zap/1)) +exten => _754XXXX,1,Gosub(ciddial\,s\,1(${EXTEN}\,707${EXTEN}\,30\,TW\,Zap/1)) +exten => _574XXXX,1,Gosub(ciddial\,s\,1(${EXTEN}\,707${EXTEN}\,30\,TW\,Zap/1)) +exten => _NXXXXXX,1,Gosub(ciddial\,s\,1(1707${EXTEN}\,707${EXTEN}\,30\,TW\,Zap/1)) +exten => 911,1,Gosub(ciddial\,s\,1(911\,911\,30\,TW\,Zap/1)) +exten => 411,1,Gosub(ciddial\,s\,1(411\,411\,30\,TW\,Zap/1)) + + +[fromSeanUniden] +include => parkedcalls +exten => 21,1,Dial(IAX2/seaniax\,20\,T) +exten => _707202XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:3}\,${EXTEN}\,30\,TW\,Zap/1)) +exten => _707219XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:3}\,${EXTEN}\,30\,TW\,Zap/1)) +exten => _707254XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:3}\,${EXTEN}\,30\,TW\,Zap/1)) +exten => _707716XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:3}\,${EXTEN}\,30\,TW\,Zap/1)) +exten => _707754XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:3}\,${EXTEN}\,30\,TW\,Zap/1)) +exten => _707574XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:3}\,${EXTEN}\,30\,TW\,Zap/1)) +exten => _NXXNXXXXXX,1,Gosub(ciddial\,s\,1(1${EXTEN}\,${EXTEN}\,30\,TW\,Zap/1)) +exten => _1NXXNXXXXXX,1,Gosub(ciddial\,s\,1(${EXTEN}\,${EXTEN:1}\,30\,TW\,Zap/1)) +exten => _754XXXX,1,Gosub(ciddial\,s\,1(${EXTEN}\,707${EXTEN}\,30\,TW\,Zap/1)) +exten => _574XXXX,1,Gosub(ciddial\,s\,1(${EXTEN}\,707${EXTEN}\,30\,TW\,Zap/1)) +exten => _NXXXXXX,1,Gosub(ciddial\,s\,1(1707${EXTEN}\,707${EXTEN}\,30\,TW\,Zap/1)) +exten => 911,1,Gosub(ciddial\,s\,1(911\,911\,30\,TW\,Zap/1)) +exten => 411,1,Gosub(ciddial\,s\,1(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) diff --git a/trunk/pbx/ael/ael-test/ref.ael-vtest17 b/trunk/pbx/ael/ael-test/ref.ael-vtest17 new file mode 100644 index 000000000..bb4204caa --- /dev/null +++ b/trunk/pbx/ael/ael-test/ref.ael-vtest17 @@ -0,0 +1,69 @@ + + +[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,Gosub(ndeep\,s\,1(100000)) +exten => 82,2,Verbose(Finished 100000 levels deep call!) +exten => 83,1,Goto(sw-2-${EXTEN}\,10) +exten => 83,2,NoOp(Finish switch-extension-2) +exten => _sw-2-.,10,Goto(83\,2) +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) + + +[ndeep] +exten => s,1,Set(LOCAL(level)=${ARG1}) +exten => s,2,GotoIf($[${level} == 0]?3:5) +exten => s,3,Verbose(2|Got to Level 0) +exten => s,4,Return() +exten => s,5,NoOp(Finish if-ndeep-3) +exten => s,6,Gosub(ndeep\,s\,1($[${level}-1])) +exten => s,7,Return() + + +[t1extension] +include => dialextens +include => dialthrus diff --git a/trunk/pbx/ael/ael-test/ref.ael-vtest21 b/trunk/pbx/ael/ael-test/ref.ael-vtest21 new file mode 100644 index 000000000..711540aa3 --- /dev/null +++ b/trunk/pbx/ael/ael-test/ref.ael-vtest21 @@ -0,0 +1,9 @@ +[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) diff --git a/trunk/pbx/ael/ael-test/runtests b/trunk/pbx/ael/ael-test/runtests new file mode 100755 index 000000000..9209f0a54 --- /dev/null +++ b/trunk/pbx/ael/ael-test/runtests @@ -0,0 +1,56 @@ +#!/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 diff --git a/trunk/pbx/ael/ael-test/setref b/trunk/pbx/ael/ael-test/setref new file mode 100755 index 000000000..b483f05ae --- /dev/null +++ b/trunk/pbx/ael/ael-test/setref @@ -0,0 +1,7 @@ +#!/bin/bash + +for i in res.*; do + refname=`echo $i | sed 's/^res/ref/'` + echo $refname + mv $i $refname +done diff --git a/trunk/pbx/dundi-parser.c b/trunk/pbx/dundi-parser.c new file mode 100644 index 000000000..bab1bfc91 --- /dev/null +++ b/trunk/pbx/dundi-parser.c @@ -0,0 +1,846 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2005, Digium, Inc. + * + * Mark Spencer <markster@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \brief Distributed Universal Number Discovery (DUNDi) + * + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include "asterisk/frame.h" +#include "asterisk/utils.h" +#include "asterisk/dundi.h" +#include "dundi-parser.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, const 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, const 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;x<sizeof(eid->eid) / 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) +{ + if (maxlen > len + 1) + maxlen = len + 1; + + snprintf(output, maxlen, "%s", (char *) value); +} + +static void dump_cbypass(char *output, int maxlen, void *value, int len) +{ + snprintf(output, maxlen, "Bypass Caches"); +} + +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) +{ + char tmp2[256]; + char tmp3[256]; + int datalen; + struct dundi_hint *hint; + if (len < sizeof(*hint)) { + snprintf(output, maxlen, "<invalid contents>"); + return; + } + + hint = (struct dundi_hint *) value;; + + datalen = len - offsetof(struct dundi_hint, data); + if (datalen > sizeof(tmp3) - 1) + datalen = sizeof(tmp3) - 1; + + memcpy(tmp3, hint->data, datalen); + tmp3[datalen] = '\0'; + + dundi_hint2str(tmp2, sizeof(tmp2), ntohs(hint->flags)); + + if (ast_strlen_zero(tmp3)) + snprintf(output, maxlen, "[%s]", tmp2); + else + snprintf(output, maxlen, "[%s] %s", tmp2, tmp3); +} + +static void dump_cause(char *output, int maxlen, void *value, int len) +{ + static char *causes[] = { + "SUCCESS", + "GENERAL", + "DYNAMIC", + "NOAUTH" , + }; + char tmp2[256]; + struct dundi_cause *cause; + int datalen; + int causecode; + + if (len < sizeof(*cause)) { + snprintf(output, maxlen, "<invalid contents>"); + return; + } + + cause = (struct dundi_cause*) value; + causecode = cause->causecode; + + datalen = len - offsetof(struct dundi_cause, desc); + if (datalen > sizeof(tmp2) - 1) + datalen = sizeof(tmp2) - 1; + + memcpy(tmp2, cause->desc, datalen); + tmp2[datalen] = '\0'; + + if (causecode < sizeof(causes) / sizeof(causes[0])) { + if (ast_strlen_zero(tmp2)) + snprintf(output, maxlen, "%s", causes[causecode]); + else + snprintf(output, maxlen, "%s: %s", causes[causecode], tmp2); + } else { + if (ast_strlen_zero(tmp2)) + snprintf(output, maxlen, "%d", causecode); + else + snprintf(output, maxlen, "%d: %s", causecode, tmp2); + } +} + +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 + ast_copy_string(output, "Invalid INT", maxlen); +} + +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 + ast_copy_string(output, "Invalid SHORT", maxlen); +} + +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 + ast_copy_string(output, "Invalid BYTE", maxlen); +} + +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]=""; + int datalen; + + if (len < sizeof(*answer)) { + snprintf(output, maxlen, "Invalid Answer"); + return; + } + + answer = (struct dundi_answer *)(value); + + datalen = len - offsetof(struct dundi_answer, data); + if (datalen > sizeof(tmp) - 1) + datalen = sizeof(tmp) - 1; + + memcpy(tmp, answer->data, datalen); + tmp[datalen] = '\0'; + + 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); +} + +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<len;x++) { + snprintf(output + strlen(output), maxlen - strlen(output) - 1, "%02x ", u[x]); + } + strncat(output + strlen(output), "]", maxlen - strlen(output) - 1); +} + +static struct dundi_ie { + int ie; + char *name; + void (*dump)(char *output, int maxlen, void *value, int len); +} ies[] = { + { DUNDI_IE_EID, "ENTITY IDENT", dump_eid }, + { DUNDI_IE_CALLED_CONTEXT, "CALLED CONTEXT", dump_string }, + { DUNDI_IE_CALLED_NUMBER, "CALLED NUMBER", dump_string }, + { DUNDI_IE_EID_DIRECT, "DIRECT EID", dump_eid }, + { DUNDI_IE_ANSWER, "ANSWER", dump_answer }, + { DUNDI_IE_TTL, "TTL", dump_short }, + { DUNDI_IE_VERSION, "VERSION", dump_short }, + { DUNDI_IE_EXPIRATION, "EXPIRATION", dump_short }, + { DUNDI_IE_UNKNOWN, "UKWN DUNDI CMD", dump_byte }, + { DUNDI_IE_CAUSE, "CAUSE", dump_cause }, + { DUNDI_IE_REQEID, "REQUEST EID", dump_eid }, + { DUNDI_IE_ENCDATA, "ENCDATA", dump_encrypted }, + { DUNDI_IE_SHAREDKEY, "SHAREDKEY", dump_raw }, + { DUNDI_IE_SIGNATURE, "SIGNATURE", dump_raw }, + { DUNDI_IE_KEYCRC32, "KEYCRC32", dump_int }, + { DUNDI_IE_HINT, "HINT", dump_hint }, + { DUNDI_IE_DEPARTMENT, "DEPARTMENT", dump_string }, + { DUNDI_IE_ORGANIZATION, "ORGANIZTN", dump_string }, + { DUNDI_IE_LOCALITY, "LOCALITY", dump_string }, + { DUNDI_IE_STATE_PROV, "STATEPROV", dump_string }, + { DUNDI_IE_COUNTRY, "COUNTRY", dump_string }, + { DUNDI_IE_EMAIL, "EMAIL", dump_string }, + { DUNDI_IE_PHONE, "PHONE", dump_string }, + { DUNDI_IE_IPADDR, "ADDRESS", dump_string }, + { DUNDI_IE_CACHEBYPASS, "CBYPASS", dump_cbypass }, +}; + +const char *dundi_ie2str(int ie) +{ + int x; + for (x=0;x<(int)sizeof(ies) / (int)sizeof(ies[0]); x++) { + if (ies[x].ie == ie) + return ies[x].name; + } + return "Unknown IE"; +} + +static void dump_ies(unsigned char *iedata, int spaces, int len) +{ + int ielen; + int ie; + int x; + int found; + char interp[1024]; + char tmp[1024]; + if (len < 2) + return; + while(len >= 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; +} diff --git a/trunk/pbx/dundi-parser.h b/trunk/pbx/dundi-parser.h new file mode 100644 index 000000000..8ff772347 --- /dev/null +++ b/trunk/pbx/dundi-parser.h @@ -0,0 +1,88 @@ +/* + * Distributed Universal Number Discovery (DUNDi) + * + * Copyright (C) 2004 - 2005, Digium Inc. + * + * Written by Mark Spencer <markster@digium.com> + * + * 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, const char *s); +extern int dundi_str_short_to_eid(dundi_eid *eid, const 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 diff --git a/trunk/pbx/pbx_ael.c b/trunk/pbx/pbx_ael.c new file mode 100644 index 000000000..cffa65f40 --- /dev/null +++ b/trunk/pbx/pbx_ael.c @@ -0,0 +1,313 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2006, Digium, Inc. + * + * Steve Murphy <murf@parsetree.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \brief Compile symbolic Asterisk Extension Logic into Asterisk extensions, version 2. + * + */ + +/*** MODULEINFO + <depend>res_ael_share</depend> + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include <ctype.h> +#include <regex.h> +#include <sys/stat.h> + +#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" +#include "asterisk/pval.h" +#ifdef AAL_ARGCHECK +#include "asterisk/argdesc.h" +#endif + +/* 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); + +#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 +*/ + +#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); +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); +void destroy_extensions(struct ael_extension *exten); +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 void substitute_commas(char *str); */ + +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); + } + 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 char *handle_cli_ael_debug_multiple(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + switch (cmd) { + case CLI_INIT: + e->command = "ael debug [read|tokens|macros|contexts|off]"; + e->usage = + "Usage: ael debug [read|tokens|macros|contexts|off]\n" + " Enable AEL read, token, macro, or context debugging,\n" + " or disable all AEL debugging messages. Note: this\n" + " currently does nothing.\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + + if (a->argc != 3) + return CLI_SHOWUSAGE; + + if (!strcasecmp(a->argv[2], "read")) + aeldebug |= DEBUG_READ; + else if (!strcasecmp(a->argv[2], "tokens")) + aeldebug |= DEBUG_TOKENS; + else if (!strcasecmp(a->argv[2], "macros")) + aeldebug |= DEBUG_MACROS; + else if (!strcasecmp(a->argv[2], "contexts")) + aeldebug |= DEBUG_CONTEXTS; + else if (!strcasecmp(a->argv[2], "off")) + aeldebug = 0; + else + return CLI_SHOWUSAGE; + + return CLI_SUCCESS; +} + +static char *handle_cli_ael_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + switch (cmd) { + case CLI_INIT: + e->command = "ael reload"; + e->usage = + "Usage: ael reload\n" + " Reloads AEL configuration.\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + + if (a->argc != 2) + return CLI_SHOWUSAGE; + + return (pbx_load_module() ? CLI_FAILURE : CLI_SUCCESS); +} + +static struct ast_cli_entry cli_ael[] = { + AST_CLI_DEFINE(handle_cli_ael_reload, "Reload AEL configuration"), + AST_CLI_DEFINE(handle_cli_ael_debug_multiple, "Enable AEL debugging flags") +}; + +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, + ); + +#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 diff --git a/trunk/pbx/pbx_config.c b/trunk/pbx/pbx_config.c new file mode 100644 index 000000000..4a7a3d368 --- /dev/null +++ b/trunk/pbx/pbx_config.c @@ -0,0 +1,1668 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2006, Digium, Inc. + * + * Mark Spencer <markster@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \brief Populate and remember extensions from static config file + * + * + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include <ctype.h> + +#include "asterisk/paths.h" /* ast_config_AST_CONFIG_DIR */ +#include "asterisk/pbx.h" +#include "asterisk/config.h" +#include "asterisk/module.h" +#include "asterisk/logger.h" +#include "asterisk/cli.h" +#include "asterisk/channel.h" /* AST_MAX_EXTENSION */ +#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; +static int extenpatternmatchnew_config = 0; + +AST_MUTEX_DEFINE_STATIC(save_dialplan_lock); + +static struct ast_context *local_contexts = NULL; + +/* + * Prototypes for our completion functions + */ +static char *complete_dialplan_remove_include(struct ast_cli_args *); +static char *complete_dialplan_add_include(struct ast_cli_args *); +static char *complete_dialplan_remove_ignorepat(struct ast_cli_args *); +static char *complete_dialplan_add_ignorepat(struct ast_cli_args *); +static char *complete_dialplan_remove_extension(struct ast_cli_args *); +static char *complete_dialplan_add_extension(struct ast_cli_args *); + +/* + * Implementation of functions provided by this module + */ + +/*! + * REMOVE INCLUDE command stuff + */ +static char *handle_cli_dialplan_remove_include(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + switch (cmd) { + case CLI_INIT: + e->command = "dialplan remove include"; + e->usage = + "Usage: dialplan remove include <context> from <context>\n" + " Remove an included context from another context.\n"; + return NULL; + case CLI_GENERATE: + return complete_dialplan_remove_include(a); + } + + if (strcmp(a->argv[4], "from")) + return CLI_SHOWUSAGE; + + if (!ast_context_remove_include(a->argv[5], a->argv[3], registrar)) { + ast_cli(a->fd, "We are not including '%s' into '%s' now\n", + a->argv[3], a->argv[5]); + return CLI_SUCCESS; + } + + ast_cli(a->fd, "Failed to remove '%s' include from '%s' context\n", + a->argv[3], a->argv[5]); + return CLI_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_rdlock_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_rdlock_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_dialplan_remove_include(struct ast_cli_args *a) +{ + int which = 0; + char *res = NULL; + int len = strlen(a->word); /* how many bytes to match */ + struct ast_context *c = NULL; + + if (a->pos == 3) { /* "dialplan remove 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_rdlock_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, a->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 > a->n) + res = strdup(i_name); + } + ast_unlock_context(c); + } + + ast_unlock_contexts(); + return res; + } else if (a->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(a->line, 3); /* skip 'dialplan' 'remove' 'include' */ + + if (a->n > 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 (a->pos == 5) { /* "dialplan remove include CTX from _X_" */ + /* + * Context from which we removing include ... + */ + char *context, *dupline, *from; + const char *s = skip_words(a->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, a->word, len)) /* not a good target */ + continue; + /* walk through all includes and check if it is our context */ + if (lookup_ci(c, context) && ++which > a->n) + res = strdup(c_name); + } + ast_unlock_contexts(); + free(context); + return res; + } + + return NULL; +} + +/*! + * REMOVE EXTENSION command stuff + */ +static char *handle_cli_dialplan_remove_extension(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + int removing_priority = 0; + char *exten, *context; + char *ret = CLI_FAILURE; + + switch (cmd) { + case CLI_INIT: + e->command = "dialplan remove extension"; + e->usage = + "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"; + return NULL; + case CLI_GENERATE: + return complete_dialplan_remove_extension(a); + } + + if (a->argc != 5 && a->argc != 4) + return CLI_SHOWUSAGE; + + /* + * Priority input checking ... + */ + if (a->argc == 5) { + char *c = a->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(a->fd, "Invalid priority '%s'\n", a->argv[4]); + return CLI_FAILURE; + } + removing_priority = atoi(a->argv[4]); + } + + if (removing_priority == 0) { + ast_cli(a->fd, "If you want to remove whole extension, please " \ + "omit priority argument\n"); + return CLI_FAILURE; + } + } + + /* XXX original overwrote argv[3] */ + /* + * Format exten@context checking ... + */ + if (split_ec(a->argv[3], &exten, &context)) + return CLI_FAILURE; /* XXX malloc failure */ + if ((!strlen(exten)) || (!(strlen(context)))) { + ast_cli(a->fd, "Missing extension or context name in third argument '%s'\n", + a->argv[3]); + free(exten); + return CLI_FAILURE; + } + + if (!ast_context_remove_extension(context, exten, removing_priority, registrar)) { + if (!removing_priority) + ast_cli(a->fd, "Whole extension %s@%s removed\n", + exten, context); + else + ast_cli(a->fd, "Extension %s@%s with priority %d removed\n", + exten, context, removing_priority); + + ret = CLI_SUCCESS; + } else { + ast_cli(a->fd, "Failed to remove extension %s@%s\n", exten, context); + ret = CLI_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 + * <tab>, 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_dialplan_remove_extension(struct ast_cli_args *a) +{ + 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(a->line, &word2, &a->pos)) { + ast_log(LOG_ERROR, "Out of free memory\n"); + return NULL; + } + a->word = word2; +#endif + + if (a->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(a->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 > a->n) { /* 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 (a->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(a->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(a->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, a->word, len) && ++which > a->n) /* 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 char *handle_cli_dialplan_add_include(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + switch (cmd) { + case CLI_INIT: + e->command = "dialplan add include"; + e->usage = + "Usage: dialplan add include <context> into <context>\n" + " Include a context in another context.\n"; + return NULL; + case CLI_GENERATE: + return complete_dialplan_add_include(a); + } + + if (a->argc != 6) /* dialplan add include CTX in CTX */ + return CLI_SHOWUSAGE; + + /* fifth arg must be 'into' ... */ + if (strcmp(a->argv[4], "into")) + return CLI_SHOWUSAGE; + + if (ast_context_add_include(a->argv[5], a->argv[3], registrar)) { + switch (errno) { + case ENOMEM: + ast_cli(a->fd, "Out of memory for context addition\n"); + break; + + case EBUSY: + ast_cli(a->fd, "Failed to lock context(s) list, please try again later\n"); + break; + + case EEXIST: + ast_cli(a->fd, "Context '%s' already included in '%s' context\n", + a->argv[3], a->argv[5]); + break; + + case ENOENT: + case EINVAL: + ast_cli(a->fd, "There is no existence of context '%s'\n", + errno == ENOENT ? a->argv[5] : a->argv[3]); + break; + + default: + ast_cli(a->fd, "Failed to include '%s' in '%s' context\n", + a->argv[3], a->argv[5]); + break; + } + return CLI_FAILURE; + } + + /* show some info ... */ + ast_cli(a->fd, "Context '%s' included in '%s' context\n", + a->argv[3], a->argv[5]); + + return CLI_SUCCESS; +} + +static char *complete_dialplan_add_include(struct ast_cli_args *a) +{ + struct ast_context *c; + int which = 0; + char *ret = NULL; + int len = strlen(a->word); + + if (a->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), a->word, len) && ++which > a->n) + ret = strdup(ast_get_context_name(c)); + ast_unlock_contexts(); + return ret; + } else if (a->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(a->line, 3); /* should not fail */ + + if (a->n != 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 (a->pos == 5) { /* 'dialplan add include CTX into _X_' (dst context) */ + char *context, *dupline, *into; + const char *s = skip_words(a->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), a->word, len) && + !lookup_ci(c, context) /* not included yet */ && + ++which > a->n) + 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 char *handle_cli_dialplan_save(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + char filename[256]; + struct ast_context *c; + struct ast_config *cfg; + struct ast_variable *v; + int incomplete = 0; /* incomplete config write? */ + FILE *output; + struct ast_flags config_flags = { 0 }; + const char *base, *slash, *file; + + switch (cmd) { + case CLI_INIT: + e->command = "dialplan save"; + e->usage = + "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"; + return NULL; + case CLI_GENERATE: + return NULL; + } + + if (! (static_config && !write_protect_config)) { + ast_cli(a->fd, + "I can't save dialplan now, see '%s' example file.\n", + config); + return CLI_FAILURE; + } + + if (a->argc != 2 && a->argc != 3) + return CLI_SHOWUSAGE; + + if (ast_mutex_lock(&save_dialplan_lock)) { + ast_cli(a->fd, + "Failed to lock dialplan saving (another proccess saving?)\n"); + return CLI_FAILURE; + } + /* XXX the code here is quite loose, a pathname with .conf in it + * is assumed to be a complete pathname + */ + if (a->argc == 3) { /* have config path. Look for *.conf */ + base = a->argv[2]; + if (!strstr(a->argv[2], ".conf")) { /*no, this is assumed to be a pathname */ + /* if filename ends with '/', do not add one */ + slash = (*(a->argv[2] + strlen(a->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", config_flags); + + /* try to lock contexts list */ + if (ast_rdlock_contexts()) { + ast_cli(a->fd, "Failed to lock contexts list\n"); + ast_mutex_unlock(&save_dialplan_lock); + ast_config_destroy(cfg); + return CLI_FAILURE; + } + + /* create new file ... */ + if (!(output = fopen(filename, "wt"))) { + ast_cli(a->fd, "Failed to create file '%s'\n", + filename); + ast_unlock_contexts(); + ast_mutex_unlock(&save_dialplan_lock); + ast_config_destroy(cfg); + return CLI_FAILURE; + } + + /* fireout general info */ + fprintf(output, "[general]\nstatic=%s\nwriteprotect=%s\nautofallthrough=%s\nclearglobalvars=%s\nextenpatternmatchnew=%s\n\n", + static_config ? "yes" : "no", + write_protect_config ? "yes" : "no", + autofallthrough_config ? "yes" : "no", + clearglobalvars_config ? "yes" : "no", + extenpatternmatchnew_config ? "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_rdlock_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 { + const char *sep, *cid; + const char *el = ast_get_extension_label(p); + char label[128] = ""; + + 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(ast_get_extension_app_data(p)) ? "" : (const char *)ast_get_extension_app_data(p))); + } + } + } + + /* 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(a->fd, "Saved dialplan is incomplete\n"); + return CLI_FAILURE; + } + + ast_cli(a->fd, "Dialplan successfully saved into '%s'\n", + filename); + return CLI_SUCCESS; +} + +/*! + * \brief ADD EXTENSION command stuff + */ +static char *handle_cli_dialplan_add_extension(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + char *whole_exten; + char *exten, *prior; + int iprior = -2; + char *cidmatch, *app, *app_data; + char *start, *end; + + switch (cmd) { + case CLI_INIT: + e->command = "dialplan add extension"; + e->usage = + "Usage: dialplan add extension <exten>,<priority>,<app>,<app-data>\n" + " into <context> [replace]\n\n" + " This command will add new extension into <context>. 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"; + return NULL; + case CLI_GENERATE: + return complete_dialplan_add_extension(a); + } + + /* check for arguments at first */ + if (a->argc != 6 && a->argc != 7) + return CLI_SHOWUSAGE; + if (strcmp(a->argv[4], "into")) + return CLI_SHOWUSAGE; + if (a->argc == 7) + if (strcmp(a->argv[6], "replace")) + return CLI_SHOWUSAGE; + + /* XXX overwrite argv[3] */ + whole_exten = a->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(a->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; + } 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 CLI_SHOWUSAGE; + + if (!app_data) + app_data=""; + if (ast_add_extension(a->argv[5], a->argc == 7 ? 1 : 0, exten, iprior, NULL, cidmatch, app, + (void *)strdup(app_data), ast_free_ptr, registrar)) { + switch (errno) { + case ENOMEM: + ast_cli(a->fd, "Out of free memory\n"); + break; + + case EBUSY: + ast_cli(a->fd, "Failed to lock context(s) list, please try again later\n"); + break; + + case ENOENT: + ast_cli(a->fd, "No existence of '%s' context\n", a->argv[5]); + break; + + case EEXIST: + ast_cli(a->fd, "Extension %s@%s with priority %s already exists\n", + exten, a->argv[5], prior); + break; + + default: + ast_cli(a->fd, "Failed to add '%s,%s,%s,%s' extension into '%s' context\n", + exten, prior, app, app_data, a->argv[5]); + break; + } + return CLI_FAILURE; + } + + if (a->argc == 7) + ast_cli(a->fd, "Extension %s@%s (%s) replace by '%s,%s,%s,%s'\n", + exten, a->argv[5], prior, exten, prior, app, app_data); + else + ast_cli(a->fd, "Extension '%s,%s,%s,%s' added into '%s' context\n", + exten, prior, app, app_data, a->argv[5]); + + return CLI_SUCCESS; +} + +/*! dialplan add extension 6123,1,Dial,IAX/212.71.138.13/6123 into local */ +static char *complete_dialplan_add_extension(struct ast_cli_args *a) +{ + int which = 0; + + if (a->pos == 4) { /* complete 'into' word ... */ + return (a->n == 0) ? strdup("into") : NULL; + } else if (a->pos == 5) { /* complete context */ + struct ast_context *c = NULL; + int len = strlen(a->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), a->word, len) && ++which > a->n) + res = strdup(ast_get_context_name(c)); + ast_unlock_contexts(); + return res; + } else if (a->pos == 6) { + return a->n == 0 ? strdup("replace") : NULL; + } + return NULL; +} + +/*! + * IGNOREPAT CLI stuff + */ +static char *handle_cli_dialplan_add_ignorepat(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + switch (cmd) { + case CLI_INIT: + e->command = "dialplan add ignorepat"; + e->usage = + "Usage: dialplan add ignorepat <pattern> into <context>\n" + " This command adds a new ignore pattern into context <context>\n" + "\n" + "Example: dialplan add ignorepat _3XX into local\n"; + return NULL; + case CLI_GENERATE: + return complete_dialplan_add_ignorepat(a); + } + + if (a->argc != 6) + return CLI_SHOWUSAGE; + + if (strcmp(a->argv[4], "into")) + return CLI_SHOWUSAGE; + + if (ast_context_add_ignorepat(a->argv[5], a->argv[3], registrar)) { + switch (errno) { + case ENOMEM: + ast_cli(a->fd, "Out of free memory\n"); + break; + + case ENOENT: + ast_cli(a->fd, "There is no existence of '%s' context\n", a->argv[5]); + break; + + case EEXIST: + ast_cli(a->fd, "Ignore pattern '%s' already included in '%s' context\n", + a->argv[3], a->argv[5]); + break; + + case EBUSY: + ast_cli(a->fd, "Failed to lock context(s) list, please, try again later\n"); + break; + + default: + ast_cli(a->fd, "Failed to add ingore pattern '%s' into '%s' context\n", + a->argv[3], a->argv[5]); + break; + } + return CLI_FAILURE; + } + + ast_cli(a->fd, "Ignore pattern '%s' added into '%s' context\n", + a->argv[3], a->argv[5]); + + return CLI_SUCCESS; +} + +static char *complete_dialplan_add_ignorepat(struct ast_cli_args *a) +{ + if (a->pos == 4) + return a->n == 0 ? strdup("into") : NULL; + else if (a->pos == 5) { + struct ast_context *c; + int which = 0; + char *dupline, *ignorepat = NULL; + const char *s; + char *ret = NULL; + int len = strlen(a->word); + + /* XXX skip first three words 'dialplan' 'add' 'ignorepat' */ + s = skip_words(a->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), a->word, len)) + continue; /* not mine */ + if (ignorepat) /* there must be one, right ? */ + found = lookup_c_ip(c, ignorepat); + if (!found && ++which > a->n) + ret = strdup(ast_get_context_name(c)); + } + + if (ignorepat) + free(ignorepat); + ast_unlock_contexts(); + return ret; + } + + return NULL; +} + +static char *handle_cli_dialplan_remove_ignorepat(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + switch (cmd) { + case CLI_INIT: + e->command = "dialplan remove ignorepat"; + e->usage = + "Usage: dialplan remove ignorepat <pattern> from <context>\n" + " This command removes an ignore pattern from context <context>\n" + "\n" + "Example: dialplan remove ignorepat _3XX from local\n"; + return NULL; + case CLI_GENERATE: + return complete_dialplan_remove_ignorepat(a); + } + + if (a->argc != 6) + return CLI_SHOWUSAGE; + + if (strcmp(a->argv[4], "from")) + return CLI_SHOWUSAGE; + + if (ast_context_remove_ignorepat(a->argv[5], a->argv[3], registrar)) { + switch (errno) { + case EBUSY: + ast_cli(a->fd, "Failed to lock context(s) list, please try again later\n"); + break; + + case ENOENT: + ast_cli(a->fd, "There is no existence of '%s' context\n", a->argv[5]); + break; + + case EINVAL: + ast_cli(a->fd, "There is no existence of '%s' ignore pattern in '%s' context\n", + a->argv[3], a->argv[5]); + break; + + default: + ast_cli(a->fd, "Failed to remove ignore pattern '%s' from '%s' context\n", + a->argv[3], a->argv[5]); + break; + } + return CLI_FAILURE; + } + + ast_cli(a->fd, "Ignore pattern '%s' removed from '%s' context\n", + a->argv[3], a->argv[5]); + return CLI_SUCCESS; +} + +static char *complete_dialplan_remove_ignorepat(struct ast_cli_args *a) +{ + struct ast_context *c; + int which = 0; + char *ret = NULL; + + if (a->pos == 3) { + int len = strlen(a->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_rdlock_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), a->word, len) && ++which > a->n) { + /* 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 (a->pos == 4) { + return a->n == 0 ? strdup("from") : NULL; + } else if (a->pos == 5) { /* XXX check this */ + char *dupline, *duplinet, *ignorepat; + int len = strlen(a->word); + + dupline = strdup(a->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_rdlock_context(c)) /* fail, skip it */ + continue; + if (!partial_match(ast_get_context_name(c), a->word, len)) + continue; + if (lookup_c_ip(c, ignorepat) && ++which > a->n) + 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 char *handle_cli_dialplan_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + switch (cmd) { + case CLI_INIT: + e->command = "dialplan reload"; + e->usage = + "Usage: dialplan reload\n" + " Reload extensions.conf without reloading any other\n" + " modules. This command does not delete global variables\n" + " unless clearglobalvars is set to yes in extensions.conf\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + + if (a->argc != 2) + return CLI_SHOWUSAGE; + + if (clearglobalvars_config) + pbx_builtin_clear_globals(); + + pbx_load_module(); + + return CLI_SUCCESS; +} + +/*! + * CLI entries for commands provided by this module + */ +static struct ast_cli_entry cli_pbx_config[] = { + AST_CLI_DEFINE(handle_cli_dialplan_add_extension, "Add new extension into context"), + AST_CLI_DEFINE(handle_cli_dialplan_remove_extension, "Remove a specified extension"), + AST_CLI_DEFINE(handle_cli_dialplan_add_ignorepat, "Add new ignore pattern"), + AST_CLI_DEFINE(handle_cli_dialplan_remove_ignorepat, "Remove ignore pattern from context"), + AST_CLI_DEFINE(handle_cli_dialplan_add_include, "Include context in other context"), + AST_CLI_DEFINE(handle_cli_dialplan_remove_include, "Remove a specified include from context"), + AST_CLI_DEFINE(handle_cli_dialplan_reload, "Reload extensions and *only* extensions") +}; + +static struct ast_cli_entry cli_dialplan_save = + AST_CLI_DEFINE(handle_cli_dialplan_save, "Save dialplan"); + +/*! + * 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; + const char *newpm; + struct ast_flags config_flags = { 0 }; + + cfg = ast_config_load(config_file, config_flags); + 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); + if ((newpm = ast_variable_retrieve(cfg, "general", "extenpatternmatchnew"))) + extenpatternmatchnew_config = ast_true(newpm); + clearglobalvars_config = ast_true(ast_variable_retrieve(cfg, "general", "clearglobalvars")); + + + 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) { + 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; + 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 '(' */ + firstp = strchr(appl, '('); + if (!firstp) { + /* No arguments */ + data = ""; + } else { + 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); + } + } + + 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_ptr, registrar)) { + ast_log(LOG_WARNING, "Unable to register extension at line %d\n", v->lineno); + } + } + free(tc); + } + } else if (!strcasecmp(v->name, "include")) { + 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")) { + 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; + + 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 = S_OR(stringp, ""); + if (ast_context_add_switch2(con, appl, data, !strcasecmp(v->name, "eswitch"), registrar)) + ast_log(LOG_WARNING, "Unable to include switch '%s' in context '%s'\n", v->value, cxt); + } else { + ast_log(LOG_WARNING, "==!!== Unknown directive: %s at line %d -- IGNORING!!!\n", v->name, v->lineno); + } + } + } + ast_config_destroy(cfg); + return 1; +} + +static void 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; + struct ast_flags config_flags = { 0 }; + + cfg = ast_config_load("users.conf", config_flags); + 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_ptr, registrar); + } else { + ast_add_extension2(con, 0, cat, 1, NULL, NULL, "Dial", strdup("${HINT}"), ast_free_ptr, 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); + pbx_set_extenpatternmatchnew(extenpatternmatchnew_config); + + return AST_MODULE_LOAD_SUCCESS; +} + +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 AST_MODULE_LOAD_SUCCESS; +} + +static int reload(void) +{ + if (clearglobalvars_config) + pbx_builtin_clear_globals(); + return pbx_load_module(); +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Text Extension Configuration", + .load = load_module, + .unload = unload_module, + .reload = reload, + ); diff --git a/trunk/pbx/pbx_dundi.c b/trunk/pbx/pbx_dundi.c new file mode 100644 index 000000000..26a3e51a2 --- /dev/null +++ b/trunk/pbx/pbx_dundi.c @@ -0,0 +1,4847 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2006, Digium, Inc. + * + * Mark Spencer <markster@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \brief Distributed Universal Number Discovery (DUNDi) + */ + +/*** MODULEINFO + <depend>zlib</depend> + <use>crypto</use> + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/network.h" +#include <sys/ioctl.h> +#include <zlib.h> +#include <sys/signal.h> +#include <pthread.h> +#include <net/if.h> + +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__Darwin__) +#include <net/if_dl.h> +#include <ifaddrs.h> +#endif + +#include "asterisk/file.h" +#include "asterisk/logger.h" +#include "asterisk/channel.h" +#include "asterisk/config.h" +#include "asterisk/pbx.h" +#include "asterisk/module.h" +#include "asterisk/frame.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/netsock.h" +#include "asterisk/crypto.h" +#include "asterisk/astdb.h" +#include "asterisk/acl.h" +#include "asterisk/aes.h" +#include "asterisk/app.h" + +#include "dundi-parser.h" + +#define MAX_RESULTS 64 + +#define MAX_PACKET_SIZE 8192 + +#define MAX_WEIGHT 59999 + +#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 + +enum { + FLAG_ISREG = (1 << 0), /*!< Transaction is register request */ + FLAG_DEAD = (1 << 1), /*!< Transaction is dead */ + FLAG_FINAL = (1 << 2), /*!< Transaction has final message sent */ + FLAG_ISQUAL = (1 << 3), /*!< Transaction is a qualification */ + FLAG_ENCRYPT = (1 << 4), /*!< Transaction is encrypted wiht ECX/DCX */ + FLAG_SENDFULLKEY = (1 << 5), /*!< Send full key on transaction */ + 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 + +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 unsigned 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 */ + ast_aes_encrypt_key ecx; /*!< AES 128 Encryption context */ + ast_aes_decrypt_key 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; + char *weightstr; + 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 */ + ast_aes_encrypt_key us_ecx; /*!< Cached AES 128 Encryption context */ + ast_aes_decrypt_key us_dcx; /*!< Cached AES 128 Decryption context */ + unsigned long them_keycrc32; /*!< CRC-32 of our key */ + ast_aes_encrypt_key them_ecx; /*!< Cached AES 128 Encryption context */ + ast_aes_decrypt_key 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; + return 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) + break; + /* Create new transaction */ + if (!(trans = create_transaction(NULL))) + break; + memcpy(&trans->addr, sin, sizeof(trans->addr)); + trans->dtrans = ntohs(hdr->strans) & 32767; + 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 s, x = 0; + char eid_str[20]; + struct ifreq ifr; + + s = socket(AF_INET, SOCK_STREAM, 0); + if (s < 0) + return; + 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)) + continue; + memcpy(&global_eid, ((unsigned char *)&ifr.ifr_hwaddr) + 2, sizeof(global_eid)); + ast_debug(1, "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_debug(1, "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 get_mapping_weight(struct dundi_mapping *map) +{ + char buf[32]; + + buf[0] = 0; + if (map->weightstr) { + pbx_substitute_variables_helper(NULL, map->weightstr, buf, sizeof(buf) - 1); + if (sscanf(buf, "%d", &map->_weight) != 1) + map->_weight = MAX_WEIGHT; + } + + return map->_weight; +} + +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 = get_mapping_weight(map); + 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_debug(1, "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;x<st->nummaps;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;x<ouranswers;x++) { + if (dr[x].weight < max) + max = dr[x].weight; + } + + if (max) { + /* If we do not have a canonical result, keep looking */ + res = dundi_lookup_internal(dr + ouranswers, MAX_RESULTS - ouranswers, NULL, st->called_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_debug(1, "Our transaction went away!\n"); + st->trans->thread = 0; + destroy_trans(st->trans, 0); + } else { + for (x=0;x<ouranswers;x++) { + /* Add answers */ + if (dr[x].expiration && (expiration > 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); + ast_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_debug(1, "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_debug(1, "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); + ast_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_debug(1, "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_debug(1, "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_debug(1, "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); + ast_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; + char eid_str[20]; + char *s; + pthread_t lookupthread; + + 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_debug(1, "Answering EID query for '%s@%s'!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), ies->reqeid), ies->called_context); + + trans->thread = 1; + if (ast_pthread_create_detached(&lookupthread, NULL, dundi_query_thread, st)) { + struct dundi_ie_data ied = { 0, }; + trans->thread = 0; + ast_log(LOG_WARNING, "Unable to create thread!\n"); + ast_free(st); + dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads"); + dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied); + return -1; + } + } else { + struct dundi_ie_data ied = { 0, }; + 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_debug(1, "Caching hint at '%s'\n", key1); + ast_db_put("dundi/cache", key2, data); + ast_debug(1, "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;x<req->respcount;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; + + 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;x<ies->anscount;x++) { + if (trans->parent->respcount < trans->parent->maxcount) { + /* Make sure it's not already there */ + for (z=0;z<trans->parent->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_debug(1, "Forwarding precache for '%s@%s'!\n", ies->called_number, ies->called_context); + trans->thread = 1; + if (ast_pthread_create_detached(&lookupthread, NULL, dundi_precache_thread, st)) { + trans->thread = 0; + ast_log(LOG_WARNING, "Unable to create thread!\n"); + ast_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); + return -1; + } + } 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; + 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_debug(1, "Answering query for '%s@%s'!\n", ies->called_number, ies->called_context); + trans->thread = 1; + if (ast_pthread_create_detached(&lookupthread, NULL, dundi_lookup_thread, st)) { + trans->thread = 0; + ast_log(LOG_WARNING, "Unable to create thread!\n"); + ast_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); + return -1; + } + } 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_debug(1, "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_debug(1, "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;z<req->respcount;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_debug(1, "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); + ast_aes_encrypt_key(key, &peer->us_ecx); + ast_aes_decrypt_key(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, ast_aes_encrypt_key *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]; + ast_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, ast_aes_decrypt_key *dcx) +{ + unsigned char lastblock[16]; + int x; + memcpy(lastblock, iv, sizeof(lastblock)); + while(len > 0) { + ast_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_debug(1, "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_debug(1, "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]; + ast_debug(1, "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_debug(1, "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); + ast_aes_decrypt_key(dst, &peer->them_dcx); + ast_aes_encrypt_key(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_debug(1, "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; + if (peer->registerexpire > -1) + 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)) { + ast_verb(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_debug(1, "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;x<ies.anscount;x++) { + if (trans->parent->respcount < trans->parent->maxcount) { + /* Make sure it's not already there */ + for (z=0;z<trans->parent->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_debug(1, "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_debug(1, "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_debug(1, "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))) { + if (pack->retransid > -1) + ast_sched_del(sched, pack->retransid); + ast_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); + if (trans->autokillid > -1) + ast_sched_del(sched, trans->autokillid); + trans->autokillid = -1; + 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_debug(1, "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 = 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); + ast_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 char *dundi_do_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + switch (cmd) { + case CLI_INIT: + e->command = "dundi debug"; + e->usage = + "Usage: dundi debug\n" + " Enables dumping of DUNDi packets for debugging purposes\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + if (a->argc != 2) + return CLI_SHOWUSAGE; + dundidebug = 1; + ast_cli(a->fd, "DUNDi Debugging Enabled\n"); + return CLI_SUCCESS; +} + +static char *dundi_do_store_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + switch (cmd) { + case CLI_INIT: + e->command = "dundi store history"; + e->usage = + "Usage: dundi store history\n" + " Enables storing of DUNDi requests and times for debugging\n" + "purposes\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + if (a->argc != 3) + return CLI_SHOWUSAGE; + global_storehistory = 1; + ast_cli(a->fd, "DUNDi History Storage Enabled\n"); + return CLI_SUCCESS; +} + +static char *dundi_flush(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + int stats = 0; + switch (cmd) { + case CLI_INIT: + e->command = "dundi flush [stats]"; + e->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"; + return NULL; + case CLI_GENERATE: + return NULL; + } + if ((a->argc < 2) || (a->argc > 3)) + return CLI_SHOWUSAGE; + if (a->argc > 2) { + if (!strcasecmp(a->argv[2], "stats")) + stats = 1; + else + return CLI_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]) + ast_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(a->fd, "DUNDi Cache Flushed\n"); + } + return CLI_SUCCESS; +} + +static char *dundi_no_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + switch (cmd) { + case CLI_INIT: + e->command = "dundi no debug"; + e->usage = + "Usage: dundi no debug\n" + " Disables dumping of DUNDi packets for debugging purposes\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + if (a->argc != 3) + return CLI_SHOWUSAGE; + dundidebug = 0; + ast_cli(a->fd, "DUNDi Debugging Disabled\n"); + return CLI_SUCCESS; +} + +static char *dundi_no_store_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + switch (cmd) { + case CLI_INIT: + e->command = "dundi no store history"; + e->usage = + "Usage: dundi no store history\n" + " Disables storing of DUNDi requests and times for debugging\n" + "purposes\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + if (a->argc != 4) + return CLI_SHOWUSAGE; + global_storehistory = 0; + ast_cli(a->fd, "DUNDi History Storage Disabled\n"); + return CLI_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 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 char *dundi_do_lookup(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + int res; + char tmp[256]; + char fs[80] = ""; + char *context; + int x; + int bypass = 0; + struct dundi_result dr[MAX_RESULTS]; + struct timeval start; + switch (cmd) { + case CLI_INIT: + e->command = "dundi lookup"; + e->usage = + "Usage: dundi lookup <number>[@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"; + return NULL; + case CLI_GENERATE: + return NULL; + } + + if ((a->argc < 3) || (a->argc > 4)) + return CLI_SHOWUSAGE; + if (a->argc > 3) { + if (!strcasecmp(a->argv[3], "bypass")) + bypass=1; + else + return CLI_SHOWUSAGE; + } + ast_copy_string(tmp, a->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(a->fd, "DUNDi lookup returned error.\n"); + else if (!res) + ast_cli(a->fd, "DUNDi lookup returned no results.\n"); + else + sort_results(dr, res); + for (x=0;x<res;x++) { + ast_cli(a->fd, "%3d. %5d %s/%s (%s)\n", x + 1, dr[x].weight, dr[x].tech, dr[x].dest, dundi_flags2str(fs, sizeof(fs), dr[x].flags)); + ast_cli(a->fd, " from %s, expires in %d s\n", dr[x].eid_str, dr[x].expiration); + } + ast_cli(a->fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start)); + return CLI_SUCCESS; +} + +static char *dundi_do_precache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + int res; + char tmp[256]; + char *context; + struct timeval start; + switch (cmd) { + case CLI_INIT: + e->command = "dundi precache"; + e->usage = + "Usage: dundi precache <number>[@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"; + return NULL; + case CLI_GENERATE: + return NULL; + } + if ((a->argc < 3) || (a->argc > 3)) + return CLI_SHOWUSAGE; + ast_copy_string(tmp, a->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(a->fd, "DUNDi precache returned error.\n"); + else if (!res) + ast_cli(a->fd, "DUNDi precache returned no error.\n"); + ast_cli(a->fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start)); + return CLI_SUCCESS; +} + +static char *dundi_do_query(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + int res; + char tmp[256]; + char *context; + dundi_eid eid; + struct dundi_entity_info dei; + switch (cmd) { + case CLI_INIT: + e->command = "dundi query"; + e->usage = + "Usage: dundi query <entity>[@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"; + return NULL; + case CLI_GENERATE: + return NULL; + } + if ((a->argc < 3) || (a->argc > 3)) + return CLI_SHOWUSAGE; + if (dundi_str_to_eid(&eid, a->argv[2])) { + ast_cli(a->fd, "'%s' is not a valid EID!\n", a->argv[2]); + return CLI_SHOWUSAGE; + } + ast_copy_string(tmp, a->argv[2], sizeof(tmp)); + context = strchr(tmp, '@'); + if (context) { + *context = '\0'; + context++; + } + res = dundi_query_eid(&dei, context, eid); + if (res < 0) + ast_cli(a->fd, "DUNDi Query EID returned error.\n"); + else if (!res) + ast_cli(a->fd, "DUNDi Query EID returned no results.\n"); + else { + ast_cli(a->fd, "DUNDi Query EID succeeded:\n"); + ast_cli(a->fd, "Department: %s\n", dei.orgunit); + ast_cli(a->fd, "Organization: %s\n", dei.org); + ast_cli(a->fd, "City/Locality: %s\n", dei.locality); + ast_cli(a->fd, "State/Province: %s\n", dei.stateprov); + ast_cli(a->fd, "Country: %s\n", dei.country); + ast_cli(a->fd, "E-mail: %s\n", dei.email); + ast_cli(a->fd, "Phone: %s\n", dei.phone); + ast_cli(a->fd, "IP Address: %s\n", dei.ipaddr); + } + return CLI_SUCCESS; +} + +static char *dundi_show_peer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + struct dundi_peer *peer; + struct permission *p; + char *order; + char eid_str[20]; + int x, cnt; + switch (cmd) { + case CLI_INIT: + e->command = "dundi show peer"; + e->usage = + "Usage: dundi show peer [peer]\n" + " Provide a detailed description of a specifid DUNDi peer.\n"; + return NULL; + case CLI_GENERATE: + return complete_peer_helper(a->line, a->word, a->pos, a->n, 3); + } + if (a->argc != 4) + return CLI_SHOWUSAGE; + AST_LIST_LOCK(&peers); + AST_LIST_TRAVERSE(&peers, peer, list) { + if (!strcasecmp(dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), a->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(a->fd, "Peer: %s\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid)); + ast_cli(a->fd, "Model: %s\n", model2str(peer->model)); + ast_cli(a->fd, "Host: %s\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "<Unspecified>"); + ast_cli(a->fd, "Dynamic: %s\n", peer->dynamic ? "yes" : "no"); + ast_cli(a->fd, "Reg: %s\n", peer->registerid < 0 ? "No" : "Yes"); + ast_cli(a->fd, "In Key: %s\n", ast_strlen_zero(peer->inkey) ? "<None>" : peer->inkey); + ast_cli(a->fd, "Out Key: %s\n", ast_strlen_zero(peer->outkey) ? "<None>" : peer->outkey); + if (!AST_LIST_EMPTY(&peer->include)) + ast_cli(a->fd, "Include logic%s:\n", peer->model & DUNDI_MODEL_OUTBOUND ? "" : " (IGNORED)"); + AST_LIST_TRAVERSE(&peer->include, p, list) + ast_cli(a->fd, "-- %s %s\n", p->allow ? "include" : "do not include", p->name); + if (!AST_LIST_EMPTY(&peer->permit)) + ast_cli(a->fd, "Query logic%s:\n", peer->model & DUNDI_MODEL_INBOUND ? "" : " (IGNORED)"); + AST_LIST_TRAVERSE(&peer->permit, p, list) + ast_cli(a->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(a->fd, "Last few query times:\n"); + ast_cli(a->fd, "-- %d. %s (%d ms)\n", x + 1, peer->lookups[x], peer->lookuptimes[x]); + cnt++; + } + } + if (cnt) + ast_cli(a->fd, "Average query time: %d ms\n", peer->avgms); + } else + ast_cli(a->fd, "No such peer '%s'\n", a->argv[3]); + AST_LIST_UNLOCK(&peers); + return CLI_SUCCESS; +} + +static char *dundi_show_peers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ +#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; + switch (cmd) { + case CLI_INIT: + e->command = "dundi show peers [registered|include|exclude|begin]"; + e->usage = + "Usage: dundi show peers [registered|include|exclude|begin]\n" + " Lists all known DUNDi peers.\n" + " If 'registered' is present, only registered peers are shown.\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + + if ((a->argc != 3) && (a->argc != 4) && (a->argc != 5)) + return CLI_SHOWUSAGE; + if ((a->argc == 4)) { + if (!strcasecmp(a->argv[3], "registered")) { + registeredonly = 1; + } else + return CLI_SHOWUSAGE; + } + AST_LIST_LOCK(&peers); + ast_cli(a->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 (a->argc == 5) { + if (!strcasecmp(a->argv[3],"include") && strstr(srch,a->argv[4])) { + print_line = -1; + } else if (!strcasecmp(a->argv[3],"exclude") && !strstr(srch,a->argv[4])) { + print_line = 1; + } else if (!strcasecmp(a->argv[3],"begin") && !strncasecmp(srch,a->argv[4],strlen(a->argv[4]))) { + print_line = -1; + } else { + print_line = 0; + } + } + + if (print_line) { + ast_cli(a->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(a->fd, "%d dundi peers [%d online, %d offline, %d unmonitored]\n", total_peers, online_peers, offline_peers, unmonitored_peers); + AST_LIST_UNLOCK(&peers); + return CLI_SUCCESS; +#undef FORMAT +#undef FORMAT2 +} + +static char *dundi_show_trans(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ +#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; + switch (cmd) { + case CLI_INIT: + e->command = "dundi show trans"; + e->usage = + "Usage: dundi show trans\n" + " Lists all known DUNDi transactions.\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + if (a->argc != 3) + return CLI_SHOWUSAGE; + AST_LIST_LOCK(&peers); + ast_cli(a->fd, FORMAT2, "Remote", "Src", "Dst", "Tx", "Rx", "Ack"); + AST_LIST_TRAVERSE(&alltrans, trans, all) { + ast_cli(a->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 CLI_SUCCESS; +#undef FORMAT +#undef FORMAT2 +} + +static char *dundi_show_entityid(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + char eid_str[20]; + switch (cmd) { + case CLI_INIT: + e->command = "dundi show entityid"; + e->usage = + "Usage: dundi show entityid\n" + " Displays the global entityid for this host.\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + if (a->argc != 3) + return CLI_SHOWUSAGE; + AST_LIST_LOCK(&peers); + dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid); + AST_LIST_UNLOCK(&peers); + ast_cli(a->fd, "Global EID for this system is '%s'\n", eid_str); + return CLI_SUCCESS; +} + +static char *dundi_show_requests(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ +#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]; + switch (cmd) { + case CLI_INIT: + e->command = "dundi show requests"; + e->usage = + "Usage: dundi show requests\n" + " Lists all known pending DUNDi requests.\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + if (a->argc != 3) + return CLI_SHOWUSAGE; + AST_LIST_LOCK(&peers); + ast_cli(a->fd, FORMAT2, "Number", "Context", "Root", "Max", "Rsp"); + AST_LIST_TRAVERSE(&requests, req, list) { + ast_cli(a->fd, FORMAT, req->number, req->dcontext, + dundi_eid_zero(&req->root_eid) ? "<unspecified>" : dundi_eid_to_str(eidstr, sizeof(eidstr), &req->root_eid), req->maxcount, req->respcount); + } + AST_LIST_UNLOCK(&peers); + return CLI_SUCCESS; +#undef FORMAT +#undef FORMAT2 +} + +/* Grok-a-dial DUNDi */ + +static char *dundi_show_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ +#define FORMAT2 "%-12.12s %-7.7s %-12.12s %-10.10s %-5.5s %-25.25s\n" +#define FORMAT "%-12.12s %-7s %-12.12s %-10.10s %-5.5s %-25.25s\n" + struct dundi_mapping *map; + char fs[256]; + char weight[8]; + switch (cmd) { + case CLI_INIT: + e->command = "dundi show mappings"; + e->usage = + "Usage: dundi show mappings\n" + " Lists all known DUNDi mappings.\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + if (a->argc != 3) + return CLI_SHOWUSAGE; + AST_LIST_LOCK(&peers); + ast_cli(a->fd, FORMAT2, "DUNDi Cntxt", "Weight", "Local Cntxt", "Options", "Tech", "Destination"); + AST_LIST_TRAVERSE(&mappings, map, list) { + snprintf(weight, sizeof(weight), "%d", get_mapping_weight(map)); + ast_cli(a->fd, FORMAT, map->dcontext, weight, + ast_strlen_zero(map->lcontext) ? "<none>" : map->lcontext, + dundi_flags2str(fs, sizeof(fs), map->options), tech2str(map->tech), map->dest); + } + AST_LIST_UNLOCK(&peers); + return CLI_SUCCESS; +#undef FORMAT +#undef FORMAT2 +} + +static char *dundi_show_precache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ +#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; + switch (cmd) { + case CLI_INIT: + e->command = "dundi show precache"; + e->usage = + "Usage: dundi show precache\n" + " Lists all known DUNDi scheduled precache updates.\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + if (a->argc != 3) + return CLI_SHOWUSAGE; + time(&now); + ast_cli(a->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(a->fd, FORMAT, qe->number, qe->context, h,m,s); + } + AST_LIST_UNLOCK(&pcq); + + return CLI_SUCCESS; +#undef FORMAT +#undef FORMAT2 +} + +static struct ast_cli_entry cli_dundi[] = { + AST_CLI_DEFINE(dundi_do_debug, "Enable DUNDi debugging"), + AST_CLI_DEFINE(dundi_no_debug, "Disable DUNDi debugging"), + AST_CLI_DEFINE(dundi_do_store_history, "Enable DUNDi historic records"), + AST_CLI_DEFINE(dundi_no_store_history, "Disable DUNDi historic records"), + AST_CLI_DEFINE(dundi_flush, "Flush DUNDi cache"), + AST_CLI_DEFINE(dundi_show_peers, "Show defined DUNDi peers"), + AST_CLI_DEFINE(dundi_show_trans, "Show active DUNDi transactions"), + AST_CLI_DEFINE(dundi_show_entityid, "Display Global Entity ID"), + AST_CLI_DEFINE(dundi_show_mappings, "Show DUNDi mappings"), + AST_CLI_DEFINE(dundi_show_precache, "Show DUNDi precache"), + AST_CLI_DEFINE(dundi_show_requests, "Show DUNDi requests"), + AST_CLI_DEFINE(dundi_show_peer, "Show info on a specific DUNDi peer"), + AST_CLI_DEFINE(dundi_do_precache, "Precache a number in DUNDi"), + AST_CLI_DEFINE(dundi_do_lookup, "Lookup a number in DUNDi"), + AST_CLI_DEFINE(dundi_do_query, "Query a DUNDi EID"), +}; + +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; + if (!(trans = ast_calloc(1, sizeof(*trans)))) + return NULL; + + 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); + if (pack->retransid > -1) + ast_sched_del(sched, pack->retransid); + if (needfree) + ast_free(pack); + else + pack->retransid = -1; +} + +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]) + ast_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); + if (trans->autokillid > -1) + ast_sched_del(sched, trans->autokillid); + trans->autokillid = -1; + if (trans->thread) { + /* If used by a thread, mark as dead and be done */ + ast_set_flag(trans, FLAG_DEAD); + } else + ast_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) + ast_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;x<trans->eidcount;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;x<mapcount;x++) + ouranswers = dundi_lookup_local(dr, maps + x, trans->parent->number, &trans->us_eid, ouranswers, &hmd); + if (ouranswers < 0) + ouranswers = 0; + for (x=0;x<ouranswers;x++) { + if (dr[x].weight < max) + max = dr[x].weight; + } + if (max) { + /* If we do not have a canonical result, keep looking */ + res = dundi_lookup_internal(dr + ouranswers, MAX_RESULTS - ouranswers, NULL, trans->parent->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;x<trans->eidcount;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<ouranswers;x++) { + /* Add answers */ + if (dr[x].expiration && (expiration > 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;x<trans->eidcount;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_debug(1, "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;x<trans->eidcount;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_debug(1, "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_debug(1, "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_debug(1, "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) { + ast_debug(1, "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_debug(1, "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_debug(1, "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 && ast_check_hangup(chan)) + 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_debug(1, "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_debug(1, "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 || !ast_check_hangup(chan))) { + /* 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 || !ast_check_hangup(chan))) { + ms = 100; + ast_waitfor_n_fd(dr.pfds, 1, &ms, NULL); + } + if (chan && ast_check_hangup(chan)) + ast_debug(1, "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(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 = NULL; + while ((con = ast_walk_contexts(con))) { + if (strcasecmp(cur->lcontext, ast_get_context_name(con))) + continue; + /* Found the match, now queue them all up */ + ast_rdlock_context(con); + e = NULL; + while ((e = ast_walk_context_extensions(con, e))) + reschedule_precache(ast_get_extension_name(e), cur->dcontext, 0); + ast_unlock_context(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_debug(1, "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, const 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 return the Technology/Resource found in the first result\n" + "in the DUNDi 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, +}; + +enum { + OPT_BYPASS_CACHE = (1 << 0), +}; + +AST_APP_OPTIONS(dundi_query_opts, BEGIN_OPTIONS + AST_APP_OPTION('b', OPT_BYPASS_CACHE), +END_OPTIONS ); + +unsigned int dundi_result_id; + +struct dundi_result_datastore { + struct dundi_result results[MAX_RESULTS]; + unsigned int num_results; + unsigned int id; +}; + +static void drds_destroy(struct dundi_result_datastore *drds) +{ + ast_free(drds); +} + +static void drds_destroy_cb(void *data) +{ + struct dundi_result_datastore *drds = data; + drds_destroy(drds); +} + +static const struct ast_datastore_info dundi_result_datastore_info = { + .type = "DUNDIQUERY", + .destroy = drds_destroy_cb, +}; + +static int dundi_query_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) +{ + struct ast_module_user *u; + AST_DECLARE_APP_ARGS(args, + AST_APP_ARG(number); + AST_APP_ARG(context); + AST_APP_ARG(options); + ); + struct ast_flags opts = { 0, }; + char *parse; + struct dundi_result_datastore *drds; + struct ast_datastore *datastore; + + u = ast_module_user_add(chan); + + if (ast_strlen_zero(data)) { + ast_log(LOG_WARNING, "DUNDIQUERY requires an argument (number)\n"); + ast_module_user_remove(u); + return -1; + } + + if (!chan) { + ast_log(LOG_ERROR, "DUNDIQUERY can not be used without a channel!\n"); + ast_module_user_remove(u); + return -1; + } + + parse = ast_strdupa(data); + + AST_STANDARD_APP_ARGS(args, parse); + + if (!ast_strlen_zero(args.options)) + ast_app_parse_options(dundi_query_opts, &opts, NULL, args.options); + + if (ast_strlen_zero(args.context)) + args.context = "e164"; + + if (!(drds = ast_calloc(1, sizeof(*drds)))) { + ast_module_user_remove(u); + return -1; + } + + drds->id = ast_atomic_fetchadd_int((int *) &dundi_result_id, 1); + snprintf(buf, len, "%u", drds->id); + + if (!(datastore = ast_channel_datastore_alloc(&dundi_result_datastore_info, buf))) { + drds_destroy(drds); + ast_module_user_remove(u); + return -1; + } + + datastore->data = drds; + + drds->num_results = dundi_lookup(drds->results, ARRAY_LEN(drds->results), NULL, args.context, + args.number, ast_test_flag(&opts, OPT_BYPASS_CACHE)); + + if (drds->num_results > 0) + sort_results(drds->results, drds->num_results); + + ast_channel_lock(chan); + ast_channel_datastore_add(chan, datastore); + ast_channel_unlock(chan); + + ast_module_user_remove(u); + + return 0; +} + +static struct ast_custom_function dundi_query_function = { + .name = "DUNDIQUERY", + .synopsis = "Initiate a DUNDi query.", + .syntax = "DUNDIQUERY(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 be a numeric ID that can be used to retrieve\n" + "the results with the DUNDIRESULT function. If the 'b' option is\n" + "is specified, the internal DUNDi cache will be bypassed.\n", + .read = dundi_query_read, +}; + +static int dundi_result_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) +{ + struct ast_module_user *u; + AST_DECLARE_APP_ARGS(args, + AST_APP_ARG(id); + AST_APP_ARG(resultnum); + ); + char *parse; + unsigned int num; + struct dundi_result_datastore *drds; + struct ast_datastore *datastore; + int res = -1; + + u = ast_module_user_add(chan); + + if (ast_strlen_zero(data)) { + ast_log(LOG_WARNING, "DUNDIRESULT requires an argument (id and resultnum)\n"); + goto finish; + } + + if (!chan) { + ast_log(LOG_ERROR, "DUNDRESULT can not be used without a channel!\n"); + goto finish; + } + + parse = ast_strdupa(data); + + AST_STANDARD_APP_ARGS(args, parse); + + if (ast_strlen_zero(args.id)) { + ast_log(LOG_ERROR, "A result ID must be provided to DUNDIRESULT\n"); + goto finish; + } + + if (ast_strlen_zero(args.resultnum)) { + ast_log(LOG_ERROR, "A result number must be given to DUNDIRESULT!\n"); + goto finish; + } + + ast_channel_lock(chan); + datastore = ast_channel_datastore_find(chan, &dundi_result_datastore_info, args.id); + ast_channel_unlock(chan); + + if (!datastore) { + ast_log(LOG_WARNING, "No DUNDi results found for query ID '%s'\n", args.id); + goto finish; + } + + drds = datastore->data; + + if (!strcasecmp(args.resultnum, "getnum")) { + snprintf(buf, len, "%u", drds->num_results); + res = 0; + goto finish; + } + + if (sscanf(args.resultnum, "%u", &num) != 1) { + ast_log(LOG_ERROR, "Invalid value '%s' for resultnum to DUNDIRESULT!\n", + args.resultnum); + goto finish; + } + + if (num && num <= drds->num_results) { + snprintf(buf, len, "%s/%s", drds->results[num - 1].tech, drds->results[num - 1].dest); + res = 0; + } else + ast_log(LOG_WARNING, "Result number %u is not valid for DUNDi query results for ID %s!\n", num, args.id); + +finish: + ast_module_user_remove(u); + + return res; +} + +static struct ast_custom_function dundi_result_function = { + .name = "DUNDIRESULT", + .synopsis = "Retrieve results from a DUNDIQUERY", + .syntax = "DUNDIRESULT(id|resultnum)", + .desc = "This function will retrieve results from a previous use\n" + "of the DUNDIQUERY function.\n" + " id - This argument is the identifier returned by the DUNDIQUERY function.\n" + " resultnum - This is the number of the result that you want to retrieve.\n" + " Results start at 1. If this argument is specified as \"getnum\",\n" + " then it will return the total number of results that are available.\n", + .read = dundi_result_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))) + ast_free(perm); +} + +static void destroy_peer(struct dundi_peer *peer) +{ + if (peer->registerid > -1) + ast_sched_del(sched, peer->registerid); + if (peer->regtrans) + destroy_trans(peer->regtrans, 0); + if (peer->qualifyid > -1) + ast_sched_del(sched, peer->qualifyid); + destroy_permissions(&peer->permit); + destroy_permissions(&peer->include); + ast_free(peer); +} + +static void destroy_map(struct dundi_mapping *map) +{ + if (map->weightstr) + ast_free(map->weightstr); + ast_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(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(list); + destroy_map(map); + } + } + AST_LIST_TRAVERSE_SAFE_END; + AST_LIST_UNLOCK(&peers); +} + +static void append_permission(struct permissionlist *permlist, const 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(const char *name, const 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 <= MAX_WEIGHT)) { + ast_copy_string(map->dest, fields[3], sizeof(map->dest)); + if ((map->tech = str2tech(fields[2]))) + map->dead = 0; + } else if (!strncmp(fields[1], "${", 2) && fields[1][strlen(fields[1]) - 1] == '}') { + map->weightstr = ast_strdup(fields[1]); + 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_debug(1, "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; + if (peer->qualifyid > -1) + ast_sched_del(sched, peer->qualifyid); + peer->qualifyid = -1; + 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); + if (peer->registerid > -1) + ast_sched_del(sched, peer->registerid); + peer->registerid = -1; + 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<res;x++) { + if (ast_test_flag(results + x, flag)) + found++; + } + if (found >= 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]; + const char *dundiargs; + 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;x<res;x++) { + if (ast_test_flag(results + x, DUNDI_FLAG_EXISTS)) { + if (!--priority) + break; + } + } + } + if (x < res) { + /* Got a hit! */ + dundiargs = pbx_builtin_getvar_helper(chan, "DUNDIDIALARGS"); + snprintf(req, sizeof(req), "%s/%s,,%s", results[x].tech, results[x].dest, + S_OR(dundiargs, "")); + dial = pbx_findapp("Dial"); + if (dial) + res = pbx_exec(chan, dial, req); + } else + res = -1; + return res; +} + +static int dundi_matchmore(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_MATCHMORE); +} + +static struct ast_switch dundi_switch = +{ + name: "DUNDi", + description: "DUNDi Discovered Dialplan Switch", + exists: dundi_exists, + canmatch: dundi_canmatch, + exec: dundi_exec, + matchmore: dundi_matchmore, +}; + +static int set_config(char *config_file, struct sockaddr_in* sin, int reload) +{ + struct ast_config *cfg; + struct ast_variable *v; + char *cat; + int x; + struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; + char hn[MAXHOSTNAMELEN] = ""; + struct ast_hostent he; + struct hostent *hp; + struct sockaddr_in sin2; + static int last_port = 0; + int globalpcmodel = 0; + dundi_eid testeid; + + if (!(cfg = ast_config_load(config_file, config_flags))) { + ast_log(LOG_ERROR, "Unable to load config %s\n", config_file); + return -1; + } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) + return 0; + + dundi_ttl = DUNDI_DEFAULT_TTL; + dundi_cache_time = DUNDI_DEFAULT_CACHE_TIME; + any_peer = NULL; + + ipaddr[0] = '\0'; + if (!gethostname(hn, sizeof(hn)-1)) { + hp = ast_gethostbyname(hn, &he); + if (hp) { + memcpy(&sin2.sin_addr, hp->h_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 (ast_str2tos(v->value, &tos)) + ast_log(LOG_WARNING, "Invalid tos value at line %d, refer to QoS documentation\n", v->lineno); + } 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); + ast_custom_function_unregister(&dundi_query_function); + ast_custom_function_unregister(&dundi_result_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; + + if (set_config("dundi.conf", &sin, 1)) + return AST_MODULE_LOAD_FAILURE; + + return AST_MODULE_LOAD_SUCCESS; +} + +static int load_module(void) +{ + 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) + return AST_MODULE_LOAD_FAILURE; + + if (set_config("dundi.conf", &sin, 0)) + 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 AST_MODULE_LOAD_FAILURE; + } + 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 AST_MODULE_LOAD_FAILURE; + } + + ast_netsock_set_qos(netsocket, tos, 0, "DUNDi"); + + if (start_network_thread()) { + ast_log(LOG_ERROR, "Unable to start network thread\n"); + close(netsocket); + return AST_MODULE_LOAD_FAILURE; + } + + ast_cli_register_multiple(cli_dundi, sizeof(cli_dundi) / sizeof(*cli_dundi)); + if (ast_register_switch(&dundi_switch)) + ast_log(LOG_ERROR, "Unable to register DUNDi switch\n"); + ast_custom_function_register(&dundi_function); + ast_custom_function_register(&dundi_query_function); + ast_custom_function_register(&dundi_result_function); + + ast_verb(2, "DUNDi Ready and Listening on %s port %d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); + + return AST_MODULE_LOAD_SUCCESS; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Distributed Universal Number Discovery (DUNDi)", + .load = load_module, + .unload = unload_module, + .reload = reload, + ); + diff --git a/trunk/pbx/pbx_gtkconsole.c b/trunk/pbx/pbx_gtkconsole.c new file mode 100644 index 000000000..5e47ac668 --- /dev/null +++ b/trunk/pbx/pbx_gtkconsole.c @@ -0,0 +1,502 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2005, Digium, Inc. + * + * Mark Spencer <markster@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \brief GTK Console monitor -- very kludgy right now + * + */ + +/*** MODULEINFO + <depend>gtk</depend> + <defaultenabled>no</defaultenabled> + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include <fcntl.h> +#include <signal.h> +#include <sys/time.h> + +#include <gtk/gtk.h> +#include <glib.h> + +#include "asterisk/pbx.h" +#include "asterisk/config.h" +#include "asterisk/module.h" +#include "asterisk/logger.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) +{ + 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 AST_MODULE_LOAD_SUCCESS; +} + +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); + ast_verb(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 AST_MODULE_LOAD_FAILURE; + } + g_thread_init(NULL); + if (gtk_init_check(NULL, NULL)) { + if (!show_console()) { + inuse++; + ast_update_use_count(); + ast_verb(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 + ast_verb(2, "GTK is not available -- skipping monitor\n"); + } + return AST_MODULE_LOAD_SUCCESS; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "GTK Console", + .load = load_module, + .unload = unload_module, + .reload = reload, + ); diff --git a/trunk/pbx/pbx_loopback.c b/trunk/pbx/pbx_loopback.c new file mode 100644 index 000000000..e6f4ed904 --- /dev/null +++ b/trunk/pbx/pbx_loopback.c @@ -0,0 +1,176 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2005, Digium, Inc. + * + * Mark Spencer <markster@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \brief Loopback PBX Module + * + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/file.h" +#include "asterisk/logger.h" +#include "asterisk/channel.h" +#include "asterisk/config.h" +#include "asterisk/pbx.h" +#include "asterisk/module.h" +#include "asterisk/frame.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 substitutes ${EXTEN}, ${CONTEXT}, and ${PRIORITY} into + the data passed to it to try 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 an extra match restriction the *original* number + must fit if specified. The "extramatch" begins with _ like an exten pattern + if it is specified. Note that the search context MUST be a different context + from the current context or the search will not succeed in an effort 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_helper(buf, sizeof(buf), exten, context, priority, data); \ + loopback_subst(&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_helper(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); + AST_LIST_HEAD_INIT_NOLOCK(&headp); + AST_LIST_INSERT_HEAD(&headp, ast_var_assign("EXTEN", exten), entries); + AST_LIST_INSERT_HEAD(&headp, ast_var_assign("CONTEXT", context), entries); + AST_LIST_INSERT_HEAD(&headp, ast_var_assign("PRIORITY", tmp), 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_subst(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) +{ + int found; + LOOPBACK_COMMON; + res = ast_spawn_extension(chan, newcontext, newexten, newpriority, callerid, &found, 0); + /* XXX hmmm... res is overridden ? */ + if (newpattern && !ast_extension_match(newpattern, exten)) + res = -1; + 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) +{ + if (ast_register_switch(&loopback_switch)) + return AST_MODULE_LOAD_FAILURE; + return AST_MODULE_LOAD_SUCCESS; +} + +AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Loopback Switch"); diff --git a/trunk/pbx/pbx_lua.c b/trunk/pbx/pbx_lua.c new file mode 100644 index 000000000..a23b31ee2 --- /dev/null +++ b/trunk/pbx/pbx_lua.c @@ -0,0 +1,1354 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2007, Digium, Inc. + * + * Matthew Nicholson <mnicholson@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! + * \file + * + * \author Matthew Nicholson <mnicholson@digium.com> + * \brief Lua PBX Switch + * + */ + +/*** MODULEINFO + <depend>lua</depend> + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/logger.h" +#include "asterisk/channel.h" +#include "asterisk/pbx.h" +#include "asterisk/module.h" +#include "asterisk/cli.h" +#include "asterisk/utils.h" +#include "asterisk/term.h" +#include "asterisk/paths.h" + +#include <lua.h> +#include <lauxlib.h> +#include <lualib.h> + +static char *config = "extensions.lua"; + +#define LUA_EXT_DATA_SIZE 256 +#define LUA_BUF_SIZE 4096 + +static char *lua_read_extensions_file(lua_State *L, long *size); +static int lua_load_extensions(lua_State *L, struct ast_channel *chan); +static int lua_reload_extensions(lua_State *L); +static void lua_free_extensions(void); +static int lua_sort_extensions(lua_State *L); +static int lua_extension_cmp(lua_State *L); +static int lua_find_extension(lua_State *L, const char *context, const char *exten, int priority, ast_switch_f *func, int push_func); +static int lua_pbx_findapp(lua_State *L); +static int lua_pbx_exec(lua_State *L); + +static int lua_get_variable_value(lua_State *L); +static int lua_set_variable_value(lua_State *L); +static int lua_get_variable(lua_State *L); +static int lua_set_variable(lua_State *L); +static int lua_func_read(lua_State *L); + +static int lua_autoservice_start(lua_State *L); +static int lua_autoservice_stop(lua_State *L); +static int lua_autoservice_status(lua_State *L); +static int lua_check_hangup(lua_State *L); + +static void lua_update_registry(lua_State *L, const char *context, const char *exten, int priority); +static void lua_push_variable_table(lua_State *L, const char *name); +static void lua_create_app_table(lua_State *L); +static void lua_create_channel_table(lua_State *L); +static void lua_create_variable_metatable(lua_State *L); +static void lua_create_application_metatable(lua_State *L); +static void lua_create_autoservice_functions(lua_State *L); +static void lua_create_hangup_function(lua_State *L); + +void lua_state_destroy(void *data); +static lua_State *lua_get_state(struct ast_channel *chan); + +static int exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data); +static int canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data); +static int matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data); +static int exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data); + +AST_MUTEX_DEFINE_STATIC(config_file_lock); +char *config_file_data = NULL; +long config_file_size = 0; + +static const struct ast_datastore_info lua_datastore = { + .type = "lua", + .destroy = lua_state_destroy, +}; + + +/*! + * \brief The destructor for lua_datastore + */ +void lua_state_destroy(void *data) +{ + if (data) + lua_close(data); +} + +/*! + * \brief [lua_CFunction] Find an app and return it in a lua table (for access from lua, don't + * call directly) + * + * This function would be called in the following example as it would be found + * in extensions.lua. + * + * \code + * app.dial + * \endcode + */ +static int lua_pbx_findapp(lua_State *L) +{ + const char *app_name = luaL_checkstring(L, 2); + + lua_newtable(L); + + lua_pushstring(L, "name"); + lua_pushstring(L, app_name); + lua_settable(L, -3); + + luaL_getmetatable(L, "application"); + lua_setmetatable(L, -2); + + return 1; +} + +/*! + * \brief [lua_CFunction] This function is part of the 'application' metatable + * and is used to execute applications similar to pbx_exec() (for access from + * lua, don't call directly) + * + * \param L the lua_State to use + * \return nothing + * + * This funciton is executed as the '()' operator for apps accessed through the + * 'app' table. + * + * \code + * app.playback('demo-congrats') + * \endcode + */ +static int lua_pbx_exec(lua_State *L) +{ + int res, nargs = lua_gettop(L); + char data[LUA_EXT_DATA_SIZE] = ""; + char *data_next = data, *app_name; + char *context, *exten; + char tmp[80], tmp2[80], tmp3[LUA_EXT_DATA_SIZE]; + int priority, autoservice; + size_t data_left = sizeof(data); + struct ast_app *app; + struct ast_channel *chan; + + lua_getfield(L, 1, "name"); + app_name = ast_strdupa(lua_tostring(L, -1)); + lua_pop(L, 1); + + if (!(app = pbx_findapp(app_name))) { + lua_pushstring(L, "application '"); + lua_pushstring(L, app_name); + lua_pushstring(L, "' not found"); + lua_concat(L, 3); + return lua_error(L); + } + + + lua_getfield(L, LUA_REGISTRYINDEX, "channel"); + chan = lua_touserdata(L, -1); + lua_pop(L, 1); + + + lua_getfield(L, LUA_REGISTRYINDEX, "context"); + context = ast_strdupa(lua_tostring(L, -1)); + lua_pop(L, 1); + + lua_getfield(L, LUA_REGISTRYINDEX, "exten"); + exten = ast_strdupa(lua_tostring(L, -1)); + lua_pop(L, 1); + + lua_getfield(L, LUA_REGISTRYINDEX, "priority"); + priority = lua_tointeger(L, -1); + lua_pop(L, 1); + + + if (nargs > 1) { + int i; + + if (!lua_isnil(L, 2)) + ast_build_string(&data_next, &data_left, "%s", luaL_checkstring(L, 2)); + + for (i = 3; i <= nargs; i++) { + if (lua_isnil(L, i)) + ast_build_string(&data_next, &data_left, ","); + else + ast_build_string(&data_next, &data_left, ",%s", luaL_checkstring(L, i)); + } + } + + ast_verb(3, "Executing [%s@%s:%d] %s(\"%s\", \"%s\")\n", + exten, context, priority, + term_color(tmp, app_name, COLOR_BRCYAN, 0, sizeof(tmp)), + term_color(tmp2, chan->name, COLOR_BRMAGENTA, 0, sizeof(tmp2)), + term_color(tmp3, data, COLOR_BRMAGENTA, 0, sizeof(tmp3))); + + lua_getfield(L, LUA_REGISTRYINDEX, "autoservice"); + autoservice = lua_toboolean(L, -1); + lua_pop(L, 1); + + if (autoservice) + ast_autoservice_stop(chan); + + res = pbx_exec(chan, app, data); + + if (autoservice) + ast_autoservice_start(chan); + + /* error executing an application, report it */ + if (res) { + lua_pushinteger(L, res); + return lua_error(L); + } + return 0; +} + +/*! + * \brief [lua_CFunction] Used to get the value of a variable or dialplan + * function (for access from lua, don't call directly) + * + * The value of the variable or function is returned. This function is the + * 'get()' function in the following example as would be seen in + * extensions.lua. + * + * \code + * channel.variable:get() + * \endcode + */ +static int lua_get_variable_value(lua_State *L) +{ + struct ast_channel *chan; + char *value = NULL, *name; + char *workspace = alloca(LUA_BUF_SIZE); + int autoservice; + + workspace[0] = '\0'; + + if (!lua_istable(L, 1)) { + lua_pushstring(L, "User probably used '.' instead of ':' for retrieving a channel variable value"); + return lua_error(L); + } + + lua_getfield(L, LUA_REGISTRYINDEX, "channel"); + chan = lua_touserdata(L, -1); + lua_pop(L, 1); + + lua_getfield(L, 1, "name"); + name = ast_strdupa(lua_tostring(L, -1)); + lua_pop(L, 1); + + lua_getfield(L, LUA_REGISTRYINDEX, "autoservice"); + autoservice = lua_toboolean(L, -1); + lua_pop(L, 1); + + if (autoservice) + ast_autoservice_stop(chan); + + /* if this is a dialplan function then use ast_func_read(), otherwise + * use pbx_retrieve_variable() */ + if (!ast_strlen_zero(name) && name[strlen(name) - 1] == ')') { + value = ast_func_read(chan, name, workspace, LUA_BUF_SIZE) ? NULL : workspace; + } else { + pbx_retrieve_variable(chan, name, &value, workspace, LUA_BUF_SIZE, &chan->varshead); + } + + if (autoservice) + ast_autoservice_start(chan); + + if (value) { + lua_pushstring(L, value); + } else { + lua_pushnil(L); + } + + return 1; +} + +/*! + * \brief [lua_CFunction] Used to set the value of a variable or dialplan + * function (for access from lua, don't call directly) + * + * This function is the 'set()' function in the following example as would be + * seen in extensions.lua. + * + * \code + * channel.variable:set("value") + * \endcode + */ +static int lua_set_variable_value(lua_State *L) +{ + const char *name, *value; + struct ast_channel *chan; + int autoservice; + + if (!lua_istable(L, 1)) { + lua_pushstring(L, "User probably used '.' instead of ':' for setting a channel variable"); + return lua_error(L); + } + + lua_getfield(L, 1, "name"); + name = ast_strdupa(lua_tostring(L, -1)); + lua_pop(L, 1); + + value = luaL_checkstring(L, 2); + + lua_getfield(L, LUA_REGISTRYINDEX, "channel"); + chan = lua_touserdata(L, -1); + lua_pop(L, 1); + + lua_getfield(L, LUA_REGISTRYINDEX, "autoservice"); + autoservice = lua_toboolean(L, -1); + lua_pop(L, 1); + + if (autoservice) + ast_autoservice_stop(chan); + + pbx_builtin_setvar_helper(chan, name, value); + + if (autoservice) + ast_autoservice_start(chan); + + return 0; +} + +/*! + * \brief Update the lua registry with the given context, exten, and priority. + * + * \param L the lua_State to use + * \param context the new context + * \param exten the new exten + * \param priority the new priority + */ +static void lua_update_registry(lua_State *L, const char *context, const char *exten, int priority) +{ + lua_pushstring(L, context); + lua_setfield(L, LUA_REGISTRYINDEX, "context"); + + lua_pushstring(L, exten); + lua_setfield(L, LUA_REGISTRYINDEX, "exten"); + + lua_pushinteger(L, priority); + lua_setfield(L, LUA_REGISTRYINDEX, "priority"); +} + +/*! + * \brief Push a 'variable' table on the stack for access the channel variable + * with the given name. + * + * \param L the lua_State to use + * \param name the name of the variable + */ +static void lua_push_variable_table(lua_State *L, const char *name) +{ + lua_newtable(L); + luaL_getmetatable(L, "variable"); + lua_setmetatable(L, -2); + + lua_pushstring(L, name); + lua_setfield(L, -2, "name"); + + lua_pushcfunction(L, &lua_get_variable_value); + lua_setfield(L, -2, "get"); + + lua_pushcfunction(L, &lua_set_variable_value); + lua_setfield(L, -2, "set"); +} + +/*! + * \brief Create the global 'app' table for executing applications + * + * \param L the lua_State to use + */ +static void lua_create_app_table(lua_State *L) +{ + lua_newtable(L); + luaL_newmetatable(L, "app"); + + lua_pushstring(L, "__index"); + lua_pushcfunction(L, &lua_pbx_findapp); + lua_settable(L, -3); + + lua_setmetatable(L, -2); + lua_setglobal(L, "app"); +} + +/*! + * \brief Create the global 'channel' table for accesing channel variables + * + * \param L the lua_State to use + */ +static void lua_create_channel_table(lua_State *L) +{ + lua_newtable(L); + luaL_newmetatable(L, "channel_data"); + + lua_pushstring(L, "__index"); + lua_pushcfunction(L, &lua_get_variable); + lua_settable(L, -3); + + lua_pushstring(L, "__newindex"); + lua_pushcfunction(L, &lua_set_variable); + lua_settable(L, -3); + + lua_setmetatable(L, -2); + lua_setglobal(L, "channel"); +} + +/*! + * \brief Create the 'variable' metatable, used to retrieve channel variables + * + * \param L the lua_State to use + */ +static void lua_create_variable_metatable(lua_State *L) +{ + luaL_newmetatable(L, "variable"); + + lua_pushstring(L, "__call"); + lua_pushcfunction(L, &lua_func_read); + lua_settable(L, -3); + + lua_pop(L, 1); +} + +/*! + * \brief Create the 'application' metatable, used to execute asterisk + * applications from lua + * + * \param L the lua_State to use + */ +static void lua_create_application_metatable(lua_State *L) +{ + luaL_newmetatable(L, "application"); + + lua_pushstring(L, "__call"); + lua_pushcfunction(L, &lua_pbx_exec); + lua_settable(L, -3); + + lua_pop(L, 1); +} + +/*! + * \brief Create the autoservice functions + * + * \param L the lua_State to use + */ +static void lua_create_autoservice_functions(lua_State *L) +{ + lua_pushcfunction(L, &lua_autoservice_start); + lua_setglobal(L, "autoservice_start"); + + lua_pushcfunction(L, &lua_autoservice_stop); + lua_setglobal(L, "autoservice_stop"); + + lua_pushcfunction(L, &lua_autoservice_status); + lua_setglobal(L, "autoservice_status"); + + lua_pushboolean(L, 0); + lua_setfield(L, LUA_REGISTRYINDEX, "autoservice"); +} + +/*! + * \brief Create the hangup check function + * + * \param L the lua_State to use + */ +static void lua_create_hangup_function(lua_State *L) +{ + lua_pushcfunction(L, &lua_check_hangup); + lua_setglobal(L, "check_hangup"); +} + +/*! + * \brief [lua_CFunction] Return a lua 'variable' object (for access from lua, don't call + * directly) + * + * This function is called to lookup a variable construct a 'variable' object. + * It would be called in the following example as would be seen in + * extensions.lua. + * + * \code + * channel.variable + * \endcode + */ +static int lua_get_variable(lua_State *L) +{ + struct ast_channel *chan; + char *name = ast_strdupa(luaL_checkstring(L, 2)); + char *value = NULL; + char *workspace = alloca(LUA_BUF_SIZE); + workspace[0] = '\0'; + + lua_getfield(L, LUA_REGISTRYINDEX, "channel"); + chan = lua_touserdata(L, -1); + lua_pop(L, 1); + + lua_push_variable_table(L, name); + + /* if this is not a request for a dialplan funciton attempt to retrieve + * the value of the variable */ + if (!ast_strlen_zero(name) && name[strlen(name) - 1] != ')') { + pbx_retrieve_variable(chan, name, &value, workspace, LUA_BUF_SIZE, &chan->varshead); + } + + if (value) { + lua_pushstring(L, value); + lua_setfield(L, -2, "value"); + } + + return 1; +} + +/*! + * \brief [lua_CFunction] Set the value of a channel variable or dialplan + * function (for access from lua, don't call directly) + * + * This function is called to set a variable or dialplan function. It would be + * called in the following example as would be seen in extensions.lua. + * + * \code + * channel.variable = "value" + * \endcode + */ +static int lua_set_variable(lua_State *L) +{ + struct ast_channel *chan; + int autoservice; + const char *name = luaL_checkstring(L, 2); + const char *value = luaL_checkstring(L, 3); + + lua_getfield(L, LUA_REGISTRYINDEX, "channel"); + chan = lua_touserdata(L, -1); + lua_pop(L, 1); + + lua_getfield(L, LUA_REGISTRYINDEX, "autoservice"); + autoservice = lua_toboolean(L, -1); + lua_pop(L, 1); + + if (autoservice) + ast_autoservice_stop(chan); + + pbx_builtin_setvar_helper(chan, name, value); + + if (autoservice) + ast_autoservice_start(chan); + + return 0; +} + +/*! + * \brief [lua_CFunction] Create a 'variable' object for accessing a dialplan + * function (for access from lua, don't call directly) + * + * This function is called to create a 'variable' object to access a dialplan + * function. It would be called in the following example as would be seen in + * extensions.lua. + * + * \code + * channel.func("arg1", "arg2", "arg3") + * \endcode + * + * To actually do anything with the resulting value you must use the 'get()' + * and 'set()' methods (the reason is the resulting value is not a value, but + * an object in the form of a lua table). + */ +static int lua_func_read(lua_State *L) +{ + int nargs = lua_gettop(L); + char fullname[LUA_EXT_DATA_SIZE] = ""; + char *fullname_next = fullname, *name; + size_t fullname_left = sizeof(fullname); + + lua_getfield(L, 1, "name"); + name = ast_strdupa(lua_tostring(L, -1)); + lua_pop(L, 1); + + ast_build_string(&fullname_next, &fullname_left, "%s(", name); + + if (nargs > 1) { + int i; + + if (!lua_isnil(L, 2)) + ast_build_string(&fullname_next, &fullname_left, "%s", luaL_checkstring(L, 2)); + + for (i = 3; i <= nargs; i++) { + if (lua_isnil(L, i)) + ast_build_string(&fullname_next, &fullname_left, ","); + else + ast_build_string(&fullname_next, &fullname_left, ",%s", luaL_checkstring(L, i)); + } + } + + ast_build_string(&fullname_next, &fullname_left, ")"); + + lua_push_variable_table(L, fullname); + + return 1; +} + +/*! + * \brief [lua_CFunction] Tell pbx_lua to maintain an autoservice on this + * channel (for access from lua, don't call directly) + * + * \param L the lua_State to use + * + * This function will set a flag that will cause pbx_lua to maintain an + * autoservice on this channel. The autoservice will automatically be stopped + * and restarted before calling applications and functions. + * + * \return This function returns the result of the ast_autoservice_start() + * function as a boolean to its lua caller. + */ +static int lua_autoservice_start(lua_State *L) +{ + struct ast_channel *chan; + int res; + + lua_getfield(L, LUA_REGISTRYINDEX, "channel"); + chan = lua_touserdata(L, -1); + lua_pop(L, 1); + + res = ast_autoservice_start(chan); + + lua_pushboolean(L, !res); + lua_setfield(L, LUA_REGISTRYINDEX, "autoservice"); + + lua_pushboolean(L, !res); + return 1; +} + +/*! + * \brief [lua_CFunction] Tell pbx_lua to stop maintaning an autoservice on + * this channel (for access from lua, don't call directly) + * + * \param L the lua_State to use + * + * This function will stop any autoservice running and turn off the autoservice + * flag. If this function returns false, it's probably because no autoservice + * was running to begin with. + * + * \return This function returns the result of the ast_autoservice_stop() + * function as a boolean to its lua caller. + */ +static int lua_autoservice_stop(lua_State *L) +{ + struct ast_channel *chan; + int res; + + lua_getfield(L, LUA_REGISTRYINDEX, "channel"); + chan = lua_touserdata(L, -1); + lua_pop(L, 1); + + res = ast_autoservice_stop(chan); + + lua_pushboolean(L, 0); + lua_setfield(L, LUA_REGISTRYINDEX, "autoservice"); + + lua_pushboolean(L, !res); + return 1; +} + +/*! + * \brief [lua_CFunction] Get the status of the autoservice flag (for access + * from lua, don't call directly) + * + * \param L the lua_State to use + * + * \return This function returns the status of the autoservice flag as a + * boolean to its lua caller. + */ +static int lua_autoservice_status(lua_State *L) +{ + lua_getfield(L, LUA_REGISTRYINDEX, "autoservice"); + return 1; +} + +/*! + * \brief [lua_CFunction] Check if this channel has been hungup or not (for + * access from lua, don't call directly) + * + * \param L the lua_State to use + * + * \return This function returns true if the channel was hungup + */ +static int lua_check_hangup(lua_State *L) +{ + struct ast_channel *chan; + lua_getfield(L, LUA_REGISTRYINDEX, "channel"); + chan = lua_touserdata(L, -1); + lua_pop(L, 1); + + lua_pushboolean(L, ast_check_hangup(chan)); + return 1; +} + +/*! + * \brief Store the sort order of each context + + * In the event of an error, an error string will be pushed onto the lua stack. + * + * \retval 0 success + * \retval 1 failure + */ +static int lua_sort_extensions(lua_State *L) +{ + int extensions, extensions_order; + + /* create the extensions_order table */ + lua_newtable(L); + lua_setfield(L, LUA_REGISTRYINDEX, "extensions_order"); + lua_getfield(L, LUA_REGISTRYINDEX, "extensions_order"); + extensions_order = lua_gettop(L); + + /* sort each context in the extensions table */ + /* load the 'extensions' table */ + lua_getglobal(L, "extensions"); + extensions = lua_gettop(L); + if (lua_isnil(L, -1)) { + lua_pop(L, 1); + lua_pushstring(L, "Unable to find 'extensions' table in extensions.lua\n"); + return 1; + } + + /* iterate through the extensions table and create a + * matching table (holding the sort order) in the + * extensions_order table for each context that is found + */ + for (lua_pushnil(L); lua_next(L, extensions); lua_pop(L, 1)) { + int context = lua_gettop(L); + int context_name = context - 1; + int context_order; + + lua_pushvalue(L, context_name); + lua_newtable(L); + context_order = lua_gettop(L); + + /* iterate through this context an popluate the corrisponding + * table in the extensions_order table */ + for (lua_pushnil(L); lua_next(L, context); lua_pop(L, 1)) { + int exten = lua_gettop(L) - 1; + + lua_pushinteger(L, lua_objlen(L, context_order) + 1); + lua_pushvalue(L, exten); + lua_settable(L, context_order); + } + lua_settable(L, extensions_order); /* put the context_order table in the extensions_order table */ + + /* now sort the new table */ + + /* push the table.sort function */ + lua_getglobal(L, "table"); + lua_getfield(L, -1, "sort"); + lua_remove(L, -2); /* remove the 'table' table */ + + /* push the context_order table */ + lua_pushvalue(L, context_name); + lua_gettable(L, extensions_order); + + /* push the comp function */ + lua_pushcfunction(L, &lua_extension_cmp); + + if (lua_pcall(L, 2, 0, 0)) { + lua_insert(L, -5); + lua_pop(L, 4); + return 1; + } + } + + /* remove the extensions table and the extensions_order table */ + lua_pop(L, 2); + return 0; +} + +/*! + * \brief [lua_CFunction] Compare two extensions (for access from lua, don't + * call directly) + * + * This function returns true if the first extension passed should match after + * the second. It behaves like the '<' operator. + */ +static int lua_extension_cmp(lua_State *L) +{ + const char *a = luaL_checkstring(L, -2); + const char *b = luaL_checkstring(L, -1); + + if (ast_extension_cmp(a, b) == -1) + lua_pushboolean(L, 1); + else + lua_pushboolean(L, 0); + + return 1; +} + +/*! + * \brief Load the extensions.lua file in to a buffer and execute the file + * + * \param L the lua_State to use + * \param size a pointer to store the size of the buffer + * + * \note The caller is expected to free the buffer at some point. + * + * \return a pointer to the buffer + */ +static char *lua_read_extensions_file(lua_State *L, long *size) +{ + FILE *f; + char *data; + char *path = alloca(strlen(config) + strlen(ast_config_AST_CONFIG_DIR) + 2); + sprintf(path, "%s/%s", ast_config_AST_CONFIG_DIR, config); + + if (!(f = fopen(path, "r"))) { + lua_pushstring(L, "cannot open '"); + lua_pushstring(L, path); + lua_pushstring(L, "' for reading: "); + lua_pushstring(L, strerror(errno)); + lua_concat(L, 4); + + return NULL; + } + + fseek(f, 0l, SEEK_END); + *size = ftell(f); + + fseek(f, 0l, SEEK_SET); + + if (!(data = ast_malloc(*size))) { + *size = 0; + fclose(f); + lua_pushstring(L, "not enough memory"); + return NULL; + } + + fread(data, sizeof(char), *size, f); + fclose(f); + + if (luaL_loadbuffer(L, data, *size, "extensions.lua") + || lua_pcall(L, 0, LUA_MULTRET, 0) + || lua_sort_extensions(L)) { + ast_free(data); + data = NULL; + *size = 0; + } + return data; +} + +/*! + * \brief Load the extensions.lua file from the internal buffer + * + * \param L the lua_State to use + * \param chan channel to work on + * + * This function also sets up some constructs used by the extensions.lua file. + * In the event of an error, an error string will be pushed onto the lua stack. + * + * \retval 0 success + * \retval 1 failure + */ +static int lua_load_extensions(lua_State *L, struct ast_channel *chan) +{ + + /* store a pointer to this channel */ + lua_pushlightuserdata(L, chan); + lua_setfield(L, LUA_REGISTRYINDEX, "channel"); + + luaL_openlibs(L); + + /* load and sort extensions */ + ast_mutex_lock(&config_file_lock); + if (luaL_loadbuffer(L, config_file_data, config_file_size, "extensions.lua") + || lua_pcall(L, 0, LUA_MULTRET, 0) + || lua_sort_extensions(L)) { + ast_mutex_unlock(&config_file_lock); + return 1; + } + ast_mutex_unlock(&config_file_lock); + + /* now we setup special tables and functions */ + + lua_create_app_table(L); + lua_create_channel_table(L); + + lua_create_variable_metatable(L); + lua_create_application_metatable(L); + + lua_create_autoservice_functions(L); + lua_create_hangup_function(L); + + return 0; +} + +/*! + * \brief Reload the extensions file and update the internal buffers if it + * loads correctly. + * + * \warning This function should not be called on a lua_State returned from + * lua_get_state(). + * + * \param L the lua_State to use (must be freshly allocated with + * luaL_newstate(), don't use lua_get_state()) + */ +static int lua_reload_extensions(lua_State *L) +{ + long size = 0; + char *data = NULL; + + luaL_openlibs(L); + + if (!(data = lua_read_extensions_file(L, &size))) { + return 1; + } + + ast_mutex_lock(&config_file_lock); + + if (config_file_data) + ast_free(config_file_data); + + config_file_data = data; + config_file_size = size; + + ast_mutex_unlock(&config_file_lock); + return 0; +} + +/*! + * \brief Free the internal extensions buffer. + */ +static void lua_free_extensions() +{ + ast_mutex_lock(&config_file_lock); + config_file_size = 0; + ast_free(config_file_data); + ast_mutex_unlock(&config_file_lock); +} + +/*! + * \brief Get the lua_State for this channel + * + * If no channel is passed then a new state is allocated. States with no + * channel assocatied with them should only be used for matching extensions. + * If the channel does not yet have a lua state associated with it, one will be + * created. + * + * \note If no channel was passed then the caller is expected to free the state + * using lua_close(). + * + * \return a lua_State + */ +static lua_State *lua_get_state(struct ast_channel *chan) +{ + struct ast_datastore *datastore = NULL; + lua_State *L; + + if (!chan) { + lua_State *L = luaL_newstate(); + if (!L) { + ast_log(LOG_ERROR, "Error allocating lua_State, no memory\n"); + return NULL; + } + + if (lua_load_extensions(L, NULL)) { + const char *error = lua_tostring(L, -1); + ast_log(LOG_ERROR, "Error loading extensions.lua: %s\n", error); + lua_close(L); + return NULL; + } + return L; + } else { + datastore = ast_channel_datastore_find(chan, &lua_datastore, NULL); + + if (!datastore) { + /* nothing found, allocate a new lua state */ + datastore = ast_channel_datastore_alloc(&lua_datastore, NULL); + if (!datastore) { + ast_log(LOG_ERROR, "Error allocation channel datastore for lua_State\n"); + return NULL; + } + + datastore->data = luaL_newstate(); + if (!datastore->data) { + ast_channel_datastore_free(datastore); + ast_log(LOG_ERROR, "Error allocating lua_State, no memory\n"); + return NULL; + } + + ast_channel_lock(chan); + ast_channel_datastore_add(chan, datastore); + ast_channel_unlock(chan); + + L = datastore->data; + + if (lua_load_extensions(L, chan)) { + const char *error = lua_tostring(L, -1); + ast_log(LOG_ERROR, "Error loading extensions.lua for %s: %s\n", chan->name, error); + + ast_channel_lock(chan); + ast_channel_datastore_remove(chan, datastore); + ast_channel_unlock(chan); + + ast_channel_datastore_free(datastore); + return NULL; + } + } + + return datastore->data; + } +} + +static int exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data) +{ + int res; + lua_State *L; + struct ast_module_user *u = ast_module_user_add(chan); + if (!u) { + ast_log(LOG_ERROR, "Error adjusting use count, probably could not allocate memory\n"); + return 0; + } + + L = lua_get_state(chan); + if (!L) { + ast_module_user_remove(u); + return 0; + } + + res = lua_find_extension(L, context, exten, priority, &exists, 0); + + if (!chan) lua_close(L); + ast_module_user_remove(u); + return res; +} + +static int canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data) +{ + int res; + lua_State *L; + struct ast_module_user *u = ast_module_user_add(chan); + if (!u) { + ast_log(LOG_ERROR, "Error adjusting use count, probably could not allocate memory\n"); + return 0; + } + + L = lua_get_state(chan); + if (!L) { + ast_module_user_remove(u); + return 0; + } + + res = lua_find_extension(L, context, exten, priority, &canmatch, 0); + + if (!chan) lua_close(L); + ast_module_user_remove(u); + return res; +} + +static int matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data) +{ + int res; + lua_State *L; + struct ast_module_user *u = ast_module_user_add(chan); + if (!u) { + ast_log(LOG_ERROR, "Error adjusting use count, probably could not allocate memory\n"); + return 0; + } + + L = lua_get_state(chan); + if (!L) { + ast_module_user_remove(u); + return 0; + } + + res = lua_find_extension(L, context, exten, priority, &matchmore, 0); + + if (!chan) lua_close(L); + ast_module_user_remove(u); + return res; +} + + +static int exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data) +{ + int res; + lua_State *L; + struct ast_module_user *u = ast_module_user_add(chan); + if (!u) { + ast_log(LOG_ERROR, "Error adjusting use count, probably could not allocate memory\n"); + return -1; + } + + L = lua_get_state(chan); + if (!L) { + ast_module_user_remove(u); + return -1; + } + + /* push the extension function onto the stack */ + if (!lua_find_extension(L, context, exten, priority, &exists, 1)) { + ast_log(LOG_ERROR, "Could not find extension %s in context %s\n", exten, context); + if (!chan) lua_close(L); + ast_module_user_remove(u); + return -1; + } + + lua_update_registry(L, context, exten, priority); + + lua_pushstring(L, context); + lua_pushstring(L, exten); + + res = lua_pcall(L, 2, 0, 0); + if (res) { + if (res == LUA_ERRRUN) { + if (lua_isnumber(L, -1)) { + res = lua_tointeger(L, -1); + } else if (lua_isstring(L, -1)) { + const char *error = lua_tostring(L, -1); + ast_log(LOG_ERROR, "Error executing lua extension: %s\n", error); + res = -1; + } + } else { + res = -1; + } + } + if (!chan) lua_close(L); + ast_module_user_remove(u); + return res; +} + +/*! + * \brief Locate an extensions and optionally push the matching function on the + * stack + * + * \param L the lua_State to use + * \param context the context to look in + * \param exten the extension to look up + * \param priority the priority to check, '1' is the only valid priority + * \param func the calling func, used to adjust matching behavior between, + * match, canmatch, and matchmore + * \param push_func whether or not to push the lua function for the given + * extension onto the stack + */ +static int lua_find_extension(lua_State *L, const char *context, const char *exten, int priority, ast_switch_f *func, int push_func) +{ + int context_table, context_order_table, i; + + ast_debug(2, "Looking up %s@%s:%i\n", exten, context, priority); + if (priority != 1) + return 0; + + /* load the 'extensions' table */ + lua_getglobal(L, "extensions"); + if (lua_isnil(L, -1)) { + ast_log(LOG_ERROR, "Unable to find 'extensions' table in extensions.lua\n"); + lua_pop(L, 1); + return 0; + } + + /* load the given context */ + lua_getfield(L, -1, context); + if (lua_isnil(L, -1)) { + lua_pop(L, 2); + return 0; + } + + /* remove the extensions table */ + lua_remove(L, -2); + + context_table = lua_gettop(L); + + /* load the extensions order table for this context */ + lua_getfield(L, LUA_REGISTRYINDEX, "extensions_order"); + lua_getfield(L, -1, context); + + lua_remove(L, -2); /* remove the extensions order table */ + + context_order_table = lua_gettop(L); + + /* step through the extensions looking for a match */ + for (i = 1; i < lua_objlen(L, context_order_table) + 1; i++) { + int e_index, isnumber, match = 0; + const char *e; + + lua_pushinteger(L, i); + lua_gettable(L, context_order_table); + e_index = lua_gettop(L); + isnumber = lua_isnumber(L, e_index); + + if (!(e = lua_tostring(L, e_index))) { + lua_pop(L, 1); + continue; + } + + /* make sure this is not the 'include' extension */ + if (!strcasecmp(e, "include")) { + lua_pop(L, 1); + continue; + } + + if (func == &matchmore) + match = ast_extension_close(e, exten, E_MATCHMORE); + else if (func == &canmatch) + match = ast_extension_close(e, exten, E_CANMATCH); + else + match = ast_extension_match(e, exten); + + /* the extension matching functions return 0 on fail, 1 on + * match, 2 on earlymatch */ + + if (!match) { + lua_pop(L, 1); + continue; /* keep trying */ + } + + if (func == &matchmore && match == 2) { + /* We match an extension ending in '!'. The decision in + * this case is final and counts as no match. */ + lua_pop(L, 3); + return 0; + } + + /* remove the context table, the context order table, and the + * extension (or replace the extension with the corisponding + * function) */ + if (push_func) { + /* here we must convert the exten back to an integer + * because lua_tostring will change the value on the + * stack to a string */ + if (isnumber) { + int e_int = lua_tointeger(L, e_index); + lua_pop(L, 1); /* the exten should be the top of the stack */ + lua_pushinteger(L, e_int); + } + lua_gettable(L, context_table); + lua_insert(L, -3); + lua_pop(L, 2); + } else { + lua_pop(L, 3); + } + + return 1; + } + + /* load the includes for this context */ + lua_getfield(L, context_table, "include"); + if (lua_isnil(L, -1)) { + lua_pop(L, 3); + return 0; + } + + /* remove the context and the order table*/ + lua_remove(L, context_order_table); + lua_remove(L, context_table); + + /* Now try any includes we have in this context */ + for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { + const char *c = lua_tostring(L, -1); + if (!c) + continue; + + if (lua_find_extension(L, c, exten, priority, func, push_func)) { + /* remove the value, the key, and the includes table + * from the stack. Leave the function behind if + * necessary */ + + if (push_func) + lua_insert(L, -4); + + lua_pop(L, 3); + return 1; + } + } + + /* pop the includes table */ + lua_pop(L, 1); + return 0; +} + +static struct ast_switch lua_switch = { + .name = "Lua", + .description = "Lua PBX Switch", + .exists = exists, + .canmatch = canmatch, + .exec = exec, + .matchmore = matchmore, +}; + + +static int load_or_reload_lua_stuff(void) +{ + int res = AST_MODULE_LOAD_SUCCESS; + + lua_State *L = luaL_newstate(); + if (!L) { + ast_log(LOG_ERROR, "Error allocating lua_State, no memory\n"); + return AST_MODULE_LOAD_DECLINE; + } + + if (lua_reload_extensions(L)) { + const char *error = lua_tostring(L, -1); + ast_log(LOG_ERROR, "Error loading extensions.lua: %s\n", error); + res = AST_MODULE_LOAD_DECLINE; + } + + lua_close(L); + return res; +} + +static int unload_module(void) +{ + ast_unregister_switch(&lua_switch); + lua_free_extensions(); + return 0; +} + +static int reload(void) +{ + return load_or_reload_lua_stuff(); +} + +static int load_module(void) +{ + int res; + + if ((res = load_or_reload_lua_stuff())) + return res; + + if (ast_register_switch(&lua_switch)) { + ast_log(LOG_ERROR, "Unable to register LUA PBX switch\n"); + return AST_MODULE_LOAD_DECLINE; + } + + return AST_MODULE_LOAD_SUCCESS; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Lua PBX Switch", + .load = load_module, + .unload = unload_module, + .reload = reload, + ); + diff --git a/trunk/pbx/pbx_realtime.c b/trunk/pbx/pbx_realtime.c new file mode 100644 index 000000000..d4876bd7f --- /dev/null +++ b/trunk/pbx/pbx_realtime.c @@ -0,0 +1,251 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2005, Digium, Inc. + * + * Mark Spencer <markster@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \brief Realtime PBX Module + * + * \arg See also: \ref AstARA + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/file.h" +#include "asterisk/logger.h" +#include "asterisk/channel.h" +#include "asterisk/config.h" +#include "asterisk/pbx.h" +#include "asterisk/module.h" +#include "asterisk/frame.h" +#include "asterisk/term.h" +#include "asterisk/manager.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]; + + appdata[0] = 0; /* just in case the substitute var func isn't called */ + if(!ast_strlen_zero(tmp)) + pbx_substitute_variables_helper(chan, tmp, appdata, sizeof(appdata) - 1); + ast_verb(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_DIALPLAN, "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) +{ + if (ast_register_switch(&realtime_switch)) + return AST_MODULE_LOAD_FAILURE; + return AST_MODULE_LOAD_SUCCESS; +} + +AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Realtime Switch"); diff --git a/trunk/pbx/pbx_spool.c b/trunk/pbx/pbx_spool.c new file mode 100644 index 000000000..113fc2b73 --- /dev/null +++ b/trunk/pbx/pbx_spool.c @@ -0,0 +1,509 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2005, Digium, Inc. + * + * Mark Spencer <markster@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \brief Full-featured outgoing call spool support + * + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include <sys/stat.h> +#include <time.h> +#include <utime.h> +#include <dirent.h> + +#include "asterisk/paths.h" /* use ast_config_AST_SPOOL_DIR */ +#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/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[AST_MAX_EXTENSION]; + char context[AST_MAX_CONTEXT]; + 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) +{ + 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) +{ + ast_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, fn); + 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; + + if ((fd = open(o->fn, O_WRONLY | O_APPEND)) < 0) + return; + + if ((f = fdopen(fd, "a"))) { + 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 (ast_mkdir(qdonedir, 0777)) { + ast_log(LOG_WARNING, "Unable to create queue directory %s -- outgoing spool archiving disabled\n", qdonedir); + unlink(o->fn); + return -1; + } + + if ((fd = open(o->fn, O_WRONLY | O_APPEND))) { + if ((f = fdopen(fd, "a"))) { + fprintf(f, "Status: %s\n", status); + fclose(f); + } else + close(fd); + } + + if (!(bname = strrchr(o->fn, '/'))) + 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)) { + ast_verb(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 { + ast_verb(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; + int ret; + + if ((ret = ast_pthread_create_detached(&t, NULL, attempt_thread, o))) { + ast_log(LOG_WARNING, "Unable to create thread :( (returned error: %d)\n", ret); + free_outgoing(o); + } +} + +static int scan_service(char *fn, time_t now, time_t atime) +{ + struct outgoing *o = NULL; + FILE *f; + int res = 0; + + if (!(o = ast_calloc(1, sizeof(*o)))) { + ast_log(LOG_WARNING, "Out of memory ;(\n"); + return -1; + } + + init_outgoing(o); + + /* Attempt to open the file */ + if (!(f = fopen(fn, "r+"))) { + remove_from_queue(o, "Failed"); + free_outgoing(o); + ast_log(LOG_WARNING, "Unable to open %s: %s, deleting\n", fn, strerror(errno)); + return -1; + } + + /* Read in and verify the contents */ + if (apply_outgoing(o, fn, f)) { + remove_from_queue(o, "Failed"); + free_outgoing(o); + ast_log(LOG_WARNING, "Invalid file contents in %s, deleting\n", fn); + fclose(f); + return -1; + } + +#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); + } + res = 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" : ""); + remove_from_queue(o, "Expired"); + free_outgoing(o); + } + + return res; +} + +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)) { + ast_log(LOG_WARNING, "Unable to stat %s\n", qdir); + continue; + } + + /* Make sure it is time for us to execute our check */ + if ((st.st_mtime == last) && (next && (next > now))) + continue; + +#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; + + if (!(dir = opendir(qdir))) { + ast_log(LOG_WARNING, "Unable to open directory %s: %s\n", qdir, strerror(errno)); + continue; + } + + while ((de = readdir(dir))) { + snprintf(fn, sizeof(fn), "%s/%s", qdir, de->d_name); + if (stat(fn, &st)) { + ast_log(LOG_WARNING, "Unable to stat %s: %s\n", fn, strerror(errno)); + continue; + } + if (!S_ISREG(st.st_mode)) + continue; + 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; + } + } + closedir(dir); + } + return NULL; +} + +static int unload_module(void) +{ + return -1; +} + +static int load_module(void) +{ + pthread_t thread; + int ret; + snprintf(qdir, sizeof(qdir), "%s/%s", ast_config_AST_SPOOL_DIR, "outgoing"); + if (ast_mkdir(qdir, 0777)) { + ast_log(LOG_WARNING, "Unable to create queue directory %s -- outgoing spool disabled\n", qdir); + return AST_MODULE_LOAD_DECLINE; + } + snprintf(qdonedir, sizeof(qdir), "%s/%s", ast_config_AST_SPOOL_DIR, "outgoing_done"); + + if ((ret = ast_pthread_create_detached_background(&thread, NULL, scan_thread, NULL))) { + ast_log(LOG_WARNING, "Unable to create thread :( (returned error: %d)\n", ret); + return AST_MODULE_LOAD_FAILURE; + } + + return AST_MODULE_LOAD_SUCCESS; +} + +AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Outgoing Spool Support"); |