; phreaknet-aux.conf - PhreakNet helper contexts ; v 1.0.16 (2022/05/17) ; *** PhreakNet Ticketing ("Billing") System [phreaknet-ticket] ; NA 20201213 ; EXTEN = # called, ARG1 = ss, ARG2 = tt, ARG3 = Charge Number (ANI), ARG4 = Calling Number, ARG5 = ticketing flags exten => _[0-9*#A-D]!,1,NoOp(DIALSTATUS: ${DIALSTATUS} / HANGUPCAUSE: ${HANGUPCAUSE} / CDR: duration: ${CDR(duration)} / billsec: ${CDR(billsec)} / start: ${CDR(start)} / answer: ${CDR(answer)} / end: ${CDR(end)}) same => n,MSet(duration=${CDR(billsec)},start=${CDR(answer)}) same => n,GotoIf(${ISNULL(${CDR(answer)})}?done) ; Don't even try to ticket the call if it wasn't answered (this includes cancelling ticketing on ANI fails, i.e. HANGUPCAUSE=29) same => n,ExecIf($["${CDR(billsec)}"="0"]?ExecIf($[${LEN(${CDR(answer)})}>0]?Set(duration=${IF($[${CDR(duration)}>0]?${CDR(duration)}:$[${STRFTIME(${EPOCH},,%s)}-${ARG1}])}))) ; edge case: calls where called called party hangs up first... same => n,ExecIf($["${CDR(billsec)}"="0"]?ExecIf($[${LEN(${CDR(answer)})}>0]?Set(start=${IF($[${CDR(duration)}>0]?${CDR(start)}:${ARG2})}))) ; ...so fall back to old way of doing it, use ss/tt, which includes ring time in billing same => n,Gosub(phreaknet-billing,${EXTEN},1(${duration},${start},${FILTER(0-9,${ARG3})},${FILTER(0-9,${ARG4})},${ARG5})) same => n(done),Set(CDR_PROP(disable)=1) same => n,Return() [phreaknet-billing] ; NA 20201213 Toll Ticketing; EXTEN= # dialed, ARG1= billing duration, ARG2= billing start time, ARG3 = Charge Num, ARG4 = Caller Num, ARG5 = type (opt.) exten => _[0-9*#A-D]!,1,ExecIf(${ISNULL(${ARG1})}?Return) ; Prevent WARNING errors if there are insufficient arguments same => n,MSet(LOCAL(duration)=${ARG1},LOCAL(start)=${FILTER(0-9,${ARG2})}) ; CDR(billsec), CDR(answer) same => n,ExecIf($["${start}"="0"]?Return) ; don't ticket calls that never supervised. The API will not do anything with them, so no point in making a pointless request. same => n,MSet(LOCAL(callee)=${FILTER(0-9,${EXTEN})},LOCAL(type)=${IF(${EXISTS(${ARG5})}?${ARG5}:direct)}) ; optional argument, default=direct same => n,Return(${CURL(https://api.phreaknet.org/v1/billing?key=${interlinkedkey}&ani=${ARG3}&caller=${ARG4}&callee=${callee}&duration=${duration}&type=${type}&start=${start})}) ; *** Outbound calling to other nodes *** [dialphreaknet-helper] ; ARG1 = #, ARG2 = (optional) trunking flags, ARG3 = (optional) destination context, ARG4 = auth attempts (0 = RSA, fallback to MD5, 1 = RSA only, 2 = MD5 only). RETURN = 1 if call failed to dial, 2 if blocked from dialing. Last updated 20220517 exten => s,1,Gosub(phreaknet-lookup,s,1(${ARG1},${ARG2})) same => n,Set(ARRAY(LOCAL(lookup),LOCAL(dopts))=${GOSUB_RETVAL}) same => n,Gosub(phreaknet-out-verify,s,1(${lookup})) same => n,ExecIf($["${GOSUB_RETVAL}"="2"]?Return(${GOSUB_RETVAL})) ; abort same => n,MSet(LOCAL(secret)=${CUT(CUT(lookup,:,2),@,1)},IAXVAR(dialed)=${ARG1}) ; aux. info for operator routings. and possibly other things same => n,GotoIf(${ISNULL(${secret})}?md5) ; routes without secrets can't be secure (this isn't really MD5, but format is the same) same => n,MSet(CHANNEL(secure_bridge_signaling)=1,CHANNEL(secure_bridge_media)=1) ; ensure the call is encrypted same => n,GotoIf($["${ARG4}"="2"]?md5) ; force MD5. If we're forcing RSA, or trying both, continue. same => n,Dial(${CUT(lookup,@,1)}:[phreaknetrsa]@${CUT(lookup,@,2-${FIELDQTY(lookup,@)})}${IF(${EXISTS(${ARG3})}?@${ARG3})},,g${IF(${DIALPLAN_EXISTS(autovonpreempted-callee,s,1)}?F(autovonpreempted-callee,${autovonchan},1))}${dopts}) ; same Dial format as a non-RSA call. Call will use RSA auth if available on the other side, otherwise silently fall back to MD5 same => n,ExecIf($["${DIALSTATUS}"="CONGESTION"|"${HANGUPCAUSE}"="0"|"${HANGUPCAUSE}"="3"|"${HANGUPCAUSE}"="20"|"${HANGUPCAUSE}"="27"|"${HANGUPCAUSE}"="50"]?NoOp(${DIALSTATUS} / ${HANGUPCAUSE}):Return(0)) ; 0, 20, and 50 are common failure codes with encryption. same => n,ExecIf($["${ARG1}"="1"]?Return(1)) ; If RSA call failed, and we should not retry with MD5, abort now. ; DBrown - 20220701 - chan_iax2 only retries with RSA, even if both sides advertise RSA and MD5. ; This will cause the call to be rejected, if the called switch does not have the appropriate RSA public key. Fall back to MD5 if this fails (if allowed to) same => n(md5),Dial(${lookup}${IF(${EXISTS(${ARG3})}?@${ARG3})},,g${IF(${DIALPLAN_EXISTS(autovonpreempted-callee,s,1)}?F(autovonpreempted-callee,${autovonchan},1))}${dopts}) same => n,Return($["${DIALSTATUS}"="CONGESTION"|"${HANGUPCAUSE}"="0"|"${HANGUPCAUSE}"="3"|"${HANGUPCAUSE}"="20"|"${HANGUPCAUSE}"="27"|"${HANGUPCAUSE}"="50"]) ; *** PhreakNet Dial Tone Routines *** ; ** Dial tone: used by local originating lines and inbound calls to the DISA alike ** [phreaknet-dialtone] exten => s,1,GotoIf(${EXISTS(${dtlocation})}?${dtlocation},1) ; shorthand extension to go back to the dial tone we were last at before... same => n,Log(WARNING,In theory you should never see this. Post a note to the mailing list if it falls through here) same => n,Goto(5551111,1) ; default dial tone. In theory, shouldn't be needed, but set this just in case. exten => _NXXXXXX,1,Set(__dtlocation=${EXTEN}) same => n,Set(CDR_PROP(disable)=1) same => n,Progress() ; the EXTEN here represents the "location" - if you have multiple different switches/different dial tones, this allows you to know which one. same => n(dt),Gosub(phreaknet-cdpr,${EXTEN},1) ; read the digits dialed same => n,GotoIf(${ISNULL(${GOSUB_RETVAL})}?phreaknet-intercept,permsig,1) ; here, we make the reasonable assumption that if the clidverif variable is not defined, this is a local peer. An easy way to keep external callers out of internal-only things. same => n,ExecIf($[${ISNULL(${clidverif})}&$[${LEN(${0-9,${GOSUB_RETVAL})})}=${LEN(${GOSUB_RETVAL})}]]?Set(DB(lastnumberdialed/${fullpeername})=${GOSUB_RETVAL})) ; save last number dialed in database for Last Number Redial, as long as it's a fully numeric number. e.g. saving *66 or ## as the last number called doesn't make sense... same => n,GotoIf(${DIALPLAN_EXISTS(phreaknet-pseudo-dialable,${GOSUB_RETVAL},1)}?phreaknet-pseudo-dialable,${GOSUB_RETVAL},1) same => n,GotoIf(${EXISTS(${clidverif})}?external) same => n(internal),Goto(phreaknet-internal-dest,${GOSUB_RETVAL},1) same => n(external),Dial(Local/${GOSUB_RETVAL}@phreaknet-dest/n,,g) same => n,Hangup() ; hang up on external callers, but you could Goto(2) or Goto(dt) to drop back to dial tone... [phreaknet-cdpr] ; "Customer Dial Pulse Receiver" as it was called on the 1ESS. This lets us read 0-9, A-D, * and #. exten => _NXXXXXX,1,Set(LOCAL(num)=${ARG1}) ; typically, ARG1 will be empty. If present, we start the CDPR past the dial tone. same => n,GotoIf(${IFMODULE(app_dialtone)}?new:old) same => n(new),Gosub(phreaknet-cdpr-dialtone,${EXTEN},1) same => n,Set(LOCAL(dt)=${GOSUB_RETVAL}) same => n,Gosub(phreaknet-cdpr-dialsounds,${EXTEN},1(${LEN(${num})})) same => n,DialTone(LOCAL(num),phreaknet-digit-map,${dt},${GOSUB_RETVAL},32,,${num},pr) same => n,Goto(complete) same => n(old),GotoIf(${EXISTS(${ARG1})}?mapcheck) same => n,Gosub(phreaknet-cdpr-dialtone,${EXTEN},1) same => n,ExecIf(${STAT(e,/var/lib/asterisk/sounds/en/${GOSUB_RETVAL}.ulaw)}?Read(LOCAL(digit),${GOSUB_RETVAL},1,nt,,0.001):Read(LOCAL(digit),dial,nti)) same => n,ExecIf(${ISNULL(${digit})}?Return) ; user dialed *nothing* (this doesn't include #). Go to permanent signal. same => n,Goto(processdigit) ; first digit received same => n(nextdigit),Gosub(phreaknet-cdpr-dialsounds,${EXTEN},1(${LEN(${num})})) same => n,ExecIf(${STAT(e,/var/lib/asterisk/sounds/en/${GOSUB_RETVAL}.ulaw)}?Read(LOCAL(digit),${GOSUB_RETVAL},1,nt,,0.001):Read(LOCAL(digit),,nt)) same => n(processdigit),GotoIf(${ISNULL(${digit})}?complete) ; either nothing was entered or # was entered. Complete call now either way. same => n,GotoIf($[$[${LEN(${FILTER(0-9,${num})})}>0]&$["${digit}"="#"]]?complete) ; # timeout completion same => n,Set(LOCAL(num)=${num}${digit}) same => n(mapcheck),GotoIf($["${EVAL_EXTEN(phreaknet-digit-map,${num},1)}"="1"]?complete) same => n,GotoIf($["${EVAL_EXTEN(phreaknet-digit-map,${num},1)}"="-5"]?:nextdigit) same => n,Read(LOCAL(digit),,1,n,,5) ; read up to 1 digit for up to 5 seconds. No audio/noise, but that's okay since it's just for this. Perhaps dialing 0 put us on a "special trunk" immediately ;) same => n,Set(LOCAL(num)=${num}${digit}) same => n,GotoIf(${ISNULL(${digit})}?complete:mapcheck) same => n(complete),Gosub(phreaknet-cdpr-dialcomplete,${EXTEN},1(${num})) ; can't use EVAL_EXTEN, because it accepts 2 inputs. same => n,ExecIf(${EXISTS(${GOSUB_RETVAL})}?Playback(${GOSUB_RETVAL},noanswer)) same => n,Return(${num}) ; as the examples above suggest, randomization can add variety and authenticity to the experience! [phreaknet-digit-map] ; A convenient lookup we can use to determine if we've gotten enough digits to complete the call. RETURN: 0 = need more digits, 1 = complete now, 2 = wait a few seconds for another (optional) digit exten => _[A-D0-9*#]!,1,Return(0) exten => ##,1,Return(1) exten => _*3XX,1,Return(1) exten => _*[14-9]X,1,Return(${ISNULL(${clidverif})}) ; local users - vertical service codes ;exten => _11XX,1,Return(${ISNULL(${clidverif})}) ; uncomment if you want to allow pulse dial phones to use vertical service codes exten => 0,1,Return(-5) ; complete call if further digits are not dialed soon exten => _11N,1,Return(-5) exten => _11[4-9]X,1,Return(1) exten => _113XX,1,Return(1) exten => _N11,1,Return(1) ; complete call immediately exten => 660,1,Return(1) exten => _95[89],1,Return(1) exten => _10[02-9]XX,1,Return(1) exten => _NXXXXXX,1,Return(1) exten => _101XXXX,1,Return(0) ; 101XXXX (FGD) exten => _101XXXX0,1,Return(-5) ; 101XXXX (FGD) exten => _101XXXX[01]NNXXXXX,1,Return(1) ; 101XXXX (FGD) exten => _1XXXXXX,1,Return(0) ; wait for 1+ exten => _[01]NXXXXXX,1,Return(1) ; 0+ or 1+ ; *** Local (Inward) Operator *** [phreaknet-inward] ; only network operator tandem and local inwards should have access to this context. include => phreaknet-inward-nonpublic include => phreaknet-inward-semipublic [from-phreaknet-operator] exten => _[0-9s]!,1,Gosub(phreaknet-verify,s,1(${EXTEN})) same => n,Goto(phreaknet-inward-semipublic,${EXTEN},1) [phreaknet-inward-nonpublic] ; only local inward operator and network operator may access these ; Busy Line Verification exten => _12[6-9]NNXXXXX,1,Gosub(phreaknet-peer,${EXTEN:-7},1) same => n,GotoIf(${ISNULL(${GOSUB_RETVAL})}?i,1) same => n,Log(NOTICE,Busy Line Verification: ${EXTEN}) same => n,ExecIf($["${EXTEN:-8:1}"="9"]?ChanSpy(${GOSUB_RETVAL},qB)) ; barge (interrupt) same => n,ExecIf($["${EXTEN:-8:1}"="8"]?ChanSpy(${GOSUB_RETVAL},qw)) ; whisper same => n,ExecIf($["${EXTEN:-8:1}"="7"]?ChanSpy(${GOSUB_RETVAL},q)) ; listen same => n,ExecIf($["${EXTEN:-8:1}"="6"]?ChanSpy(${GOSUB_RETVAL},qW)) ; talk? same => n,Hangup() [phreaknet-inward-out] exten => _X!,1,Gosub(dialphreaknet,s,1(${EXTEN},o)) ; o = operator flag same => n,Hangup() ; *** PhreakNet Custom Calling Features [phreaknet-predial] ; ARG1 = cadence exten => s,1,ExecIf($[${EXISTS(${ARG1})}&${IFMODULE(app_predial.so)}]?PreDial(c(${ARG1}))) same => n,Return() [phreaknet-busy-callback] ; This is a simple recreation of the popular *66 "Busy Redial" / "Repeat Dialing" CLASS feature, which works *between* PhreakNet nodes, too! include => invalidincoming exten => _NNXXXXX,1,RequestCallback(phreaknet-subscriber-lines,phreaknet-internal-dest,phreaknet-hints,phreaknet-busy-check-remote,${EXTEN}) same => n,GotoIf($["${CALLBACK_REQUEST_STATUS}"="IDLE"]?phreaknet-internal-dest,${EXTEN},1) ; line is idle, complete call. same => n,GotoIf($["${CALLBACK_REQUEST_STATUS}"="QUEUED"|"${CALLBACK_REQUEST_STATUS}"="ANOTHER"]?busy,1) ; callback successfully queued. same => n,PlayTones(reorder) ; sorry, can't do anything. same => n,Wait(4) same => n,Goto(phreaknet-dialtone,s,1) ; done, back to dial tone exten => busy,1,Playback(busy-pls-hold) ; this isn't a great recording, you may want to replace this same => n,Goto(phreaknet-dialtone,s,1) ; done, back to dial tone [phreaknet-busy-check-remote] exten => _NNXXXXX,1,Gosub(dialphreaknet-helper,s,1(*66${EXTEN})) same => n,Hangup(${HANGUPCAUSE}) [phreaknet-class] ; for incoming calls from other nodes, trying to query the status of a line here exten => _*66NNXXXXX,1,Progress() same => n,Gosub(phreaknet-peer,${EXTEN:-7},1) same => n,ExecIf(${ISNULL(${GOSUB_RETVAL})}?Hangup(6)) ; AST_CAUSE_CHANNEL_UNACCEPTABLE (no or unsupported device) same => n,Set(state=${DEVICE_STATE(${GOSUB_RETVAL})}) same => n,SendText(${state}) same => n,GotoIf($["${state}"="BUSY"|"${state}"="INUSE"|"${state}"="RINGING"|"${state}"="RINGINUSE"|"${state}"="ONHOLD"]?busy) same => n,GotoIf($["${state}"="NOT_INUSE"]?free) same => n,Goto(free) ; default same => n(busy),Hangup(17) ; AST_CAUSE_USER_BUSY same => n(free),Hangup(7) ; AST_CAUSE_CALL_AWARDED_DELIVERED