From d236762fc103aee1c35a1cfe5a80bd3f6925fe2c Mon Sep 17 00:00:00 2001 From: marian Date: Sat, 21 Aug 2010 18:34:01 +0000 Subject: [PATCH] Support receiving overlapped dialing on ISUP. Send extra Called Party Number digits in a SAM message. The overlapped dialing script accepts an initial number. Merged branch paulc/pstn -c 3485 git-svn-id: http://voip.null.ro/svn/yate@3493 acf43c95-373e-0410-b603-e72c3f656dc1 --- conf.d/ysigchan.conf.sample | 4 ++ libs/ysig/isup.cpp | 104 ++++++++++++++++++++++++++- libs/ysig/q931.cpp | 1 - libs/ysig/sigcall.cpp | 1 + libs/ysig/yatesig.h | 21 +++++- modules/server/ysigchan.cpp | 90 ++++++++++++++++++++--- share/scripts/overlapped.php | 136 +++++++++++++++++++++++++++++------ 7 files changed, 321 insertions(+), 36 deletions(-) diff --git a/conf.d/ysigchan.conf.sample b/conf.d/ysigchan.conf.sample index 86697aa5..164997b7 100644 --- a/conf.d/ysigchan.conf.sample +++ b/conf.d/ysigchan.conf.sample @@ -331,6 +331,10 @@ ; 0..255 Explicit numeric value, gets truncated to the SLS bit range ;sls=last (cic if pointcodetype is ITU) +; maxcalleddigits: integer: Maximum number of digits in an IAM Called Party Number +; If the called number is longer the extra digits will be sent in a SAM message +;maxcalleddigits=16 + ; numplan: string: Default numbering plan for outgoing calls ; Values: unknown, isdn, data, telex, national, private ; Defaults to unknown if missing or incorrect diff --git a/libs/ysig/isup.cpp b/libs/ysig/isup.cpp index b81c379a..76bb7758 100644 --- a/libs/ysig/isup.cpp +++ b/libs/ysig/isup.cpp @@ -751,6 +751,14 @@ static unsigned char encodeDigits(const SS7ISUP* isup, SS7MSU& msu, return setDigits(msu,val ? val->c_str() : 0,nai,b2,-1,b0); } +// Special encoder for subsequent number +static unsigned char encodeSubseq(const SS7ISUP* isup, SS7MSU& msu, + unsigned char* buf, const IsupParam* param, const NamedString* val, + const NamedList* extra, const String& prefix) +{ + return setDigits(msu,val ? val->c_str() : 0,0); +} + // Encoder for circuit group range and status (Q.763 3.43) static unsigned char encodeRangeSt(const SS7ISUP* isup, SS7MSU& msu, unsigned char* buf, const IsupParam* param, const NamedString* val, @@ -1252,7 +1260,7 @@ static const IsupParam s_paramDefs[] = { MAKE_PARAM(RemoteOperations, 0,0, 0, 0), // 3.48 MAKE_PARAM(ServiceActivation, 0,0, 0, 0), // 3.49 MAKE_PARAM(SignallingPointCode, 0,0, 0, 0), // 3.50 - MAKE_PARAM(SubsequentNumber, 0,decodeSubseq, 0, 0), // 3.51 + MAKE_PARAM(SubsequentNumber, 0,decodeSubseq, encodeSubseq, 0), // 3.51 MAKE_PARAM(SuspendResumeIndicators, 0,0, 0, 0), // 3.52 MAKE_PARAM(TransitNetworkSelection, 0,0, 0, 0), // 3.53 MAKE_PARAM(TransmissionMediumRequirement, 1,decodeInt, encodeInt, s_dict_mediumReq), // 3.54 @@ -1932,6 +1940,12 @@ static int transmitCNF(SS7ISUP* isup, unsigned int cic, const SS7Label& label, b } +// Utility used to check for called number completion +static inline bool isCalledIncomplete(const NamedList& l, const String& p = "CalledPartyNumber") +{ + return !l[p].endsWith("."); +} + /** * SS7ISUPCall */ @@ -1949,6 +1963,7 @@ SS7ISUPCall::SS7ISUPCall(SS7ISUP* controller, SignallingCircuit* cic, m_inbandAvailable(false), m_iamMsg(0), m_sgmMsg(0), + m_sentSamDigits(0), m_relTimer(300000), // Q.764: T5 - 5..15 minutes m_iamTimer(20000), // Q.764: T7 - 20..30 seconds m_sgmRecvTimer(3000) // Q.764: T34 - 2..4 seconds @@ -2043,6 +2058,7 @@ SignallingEvent* SS7ISUPCall::getEvent(const Time& when) const char* sgmParam = "OptionalBackwardCallIndicators"; if (msg->type() == SS7MsgISUP::IAM) { copyParamIAM(msg); + setOverlapped(isCalledIncomplete(msg->params())); sgmParam = "OptionalForwardCallIndicators"; } // Check segmentation. Keep the message and start timer if segmented @@ -2055,7 +2071,9 @@ SignallingEvent* SS7ISUPCall::getEvent(const Time& when) processSegmented(0,false); break; case SS7MsgISUP::SAM: + setOverlapped(isCalledIncomplete(msg->params(),"SubsequentNumber")); msg->params().addParam("tone",msg->params().getValue("SubsequentNumber")); + msg->params().addParam("dialing",String::boolText(true)); m_lastEvent = new SignallingEvent(SignallingEvent::Info,msg,this); break; case SS7MsgISUP::RLC: @@ -2101,6 +2119,9 @@ SignallingEvent* SS7ISUPCall::getEvent(const Time& when) default: ; } } + // Reset overlapped if our state is greater then Setup + if (m_state > Setup) + setOverlapped(false,false); // Check circuit event if (!m_lastEvent && m_circuit) { SignallingCircuitEvent* cicEvent = m_circuit->getEvent(when); @@ -2153,6 +2174,16 @@ bool SS7ISUPCall::sendEvent(SignallingEvent* event) } m_iamMsg = new SS7MsgISUP(SS7MsgISUP::IAM,id()); copyParamIAM(m_iamMsg,true,event->message()); + // Update overlap + setOverlapped(m_iamMsg->params()); + if (m_overlap) { + // Check for maximum number of digits allowed + String* called = m_iamMsg->params().getParam("CalledPartyNumber"); + if (called && called->length() > isup()->m_maxCalledDigits) { + m_samDigits = called->substr(isup()->m_maxCalledDigits); + *called = called->substr(0,isup()->m_maxCalledDigits); + } + } result = transmitIAM(); } break; @@ -2217,6 +2248,12 @@ bool SS7ISUPCall::sendEvent(SignallingEvent* event) } break; case SignallingEvent::Info: + if (validMsgState(true,SS7MsgISUP::SAM)) { + mylock.drop(); + transmitSAM(event->message()->params().getValue("tone")); + result = true; + break; + } //case SignallingEvent::Message: //case SignallingEvent::Transfer: default: @@ -2224,6 +2261,9 @@ bool SS7ISUPCall::sendEvent(SignallingEvent* event) "Call(%u). sendEvent not implemented for '%s' [%p]", id(),event->name(),this); } + // Reset overlapped if our state is greater then Setup + if (m_state > Setup) + setOverlapped(false,false); XDebug(isup(),DebugAll,"Call(%u). Event (%p,'%s') sent. Result: %s [%p]", id(),event,event->name(),String::boolText(result),this); mylock.drop(); @@ -2429,7 +2469,7 @@ bool SS7ISUPCall::validMsgState(bool send, SS7MsgISUP::Type type) break; return true; case SS7MsgISUP::SAM: // Subsequent address - if (m_state != Setup) + if (m_state != Setup || !m_overlap || send != outgoing()) break; return true; case SS7MsgISUP::REL: // Release @@ -2504,7 +2544,49 @@ bool SS7ISUPCall::transmitIAM() m_state = Setup; m_iamMsg->m_cic = id(); m_iamMsg->ref(); - return transmitMessage(m_iamMsg); + // Reset SAM digits: this might be a re-send + m_sentSamDigits = 0; + bool ok = transmitMessage(m_iamMsg); + if (ok && m_overlap) + transmitSAM(); + return ok; +} + +// Transmit SAM digits +bool SS7ISUPCall::transmitSAM(const char* extra) +{ + if (!m_overlap) + return false; + m_samDigits << extra; + while (m_samDigits.length() > m_sentSamDigits) { + unsigned int send = m_samDigits.length() - m_sentSamDigits; + if (send > isup()->m_maxCalledDigits) + send = isup()->m_maxCalledDigits; + SS7MsgISUP* m = new SS7MsgISUP(SS7MsgISUP::SAM,id()); + String number = m_samDigits.substr(m_sentSamDigits,send); + m->params().addParam("SubsequentNumber",number); + bool complete = !isCalledIncomplete(m->params(),"SubsequentNumber"); + bool ok = transmitMessage(m); + if (ok) { + m_sentSamDigits += send; + if (complete) { + if (m_samDigits.length() > m_sentSamDigits) + Debug(isup(),DebugNote, + "Call(%u). Completed number sending remaining='%s' [%p]", + id(),m_samDigits.substr(m_sentSamDigits).c_str(),this); + // Reset overlap sending + setOverlapped(false); + break; + } + } + else { + Debug(isup(),DebugNote,"Call(%u). Failed to send SAM with '%s' [%p]", + id(),number.c_str(),this); + complete = false; + break; + } + } + return true; } bool SS7ISUPCall::needsTesting(const SS7MsgISUP* msg) @@ -2598,6 +2680,7 @@ SignallingEvent* SS7ISUPCall::processSegmented(SS7MsgISUP* sgm, bool timeout) } connectCircuit(); m_state = Setup; + m_sgmMsg->params().setParam("overlapped",String::boolText(m_overlap)); m_lastEvent = new SignallingEvent(SignallingEvent::NewCall,m_sgmMsg,this); break; case SS7MsgISUP::CCR: @@ -2700,6 +2783,17 @@ SS7ISUP* SS7ISUPCall::isup() const return static_cast(SignallingCall::controller()); } +// Set overlapped flag. Output a debug message +void SS7ISUPCall::setOverlapped(bool on, bool numberComplete) +{ + if (m_overlap == on) + return; + m_overlap = on; + const char* reason = on ? "" : (numberComplete ? " (number complete)" : " (state changed)"); + Debug(isup(),DebugAll,"Call(%u). Overlapped dialing is %s%s [%p]", + id(),String::boolText(on),reason,this); +} + /** * SS7ISUP @@ -2716,6 +2810,7 @@ SS7ISUP::SS7ISUP(const NamedList& params, unsigned char sio) m_earlyAcm(true), m_inn(false), m_defaultSls(SlsLatest), + m_maxCalledDigits(16), m_l3LinkUp(false), m_uptTimer(0), m_userPartAvail(true), @@ -2799,6 +2894,9 @@ SS7ISUP::SS7ISUP(const NamedList& params, unsigned char sio) m_continuity = params.getValue("continuity"); m_defaultSls = params.getIntValue("sls",s_dict_callSls,m_defaultSls); + m_maxCalledDigits = params.getIntValue("maxcalleddigits",m_maxCalledDigits); + if (m_maxCalledDigits < 1) + m_maxCalledDigits = 16; setDebug(params.getBoolValue("print-messages",false), params.getBoolValue("extended-debug",false)); diff --git a/libs/ysig/q931.cpp b/libs/ysig/q931.cpp index a575bb17..b57c7099 100644 --- a/libs/ysig/q931.cpp +++ b/libs/ysig/q931.cpp @@ -713,7 +713,6 @@ ISDNQ931Call::ISDNQ931Call(ISDNQ931* controller, bool outgoing, m_callRefLen(callRefLen), m_tei(tei), m_circuit(0), - m_overlap(false), m_circuitChange(false), m_channelIDSent(false), m_rspBearerCaps(false), diff --git a/libs/ysig/sigcall.cpp b/libs/ysig/sigcall.cpp index 573c0412..d57fc2e7 100644 --- a/libs/ysig/sigcall.cpp +++ b/libs/ysig/sigcall.cpp @@ -240,6 +240,7 @@ void SignallingCallControl::removeCall(SignallingCall* call, bool del) SignallingCall::SignallingCall(SignallingCallControl* controller, bool outgoing, bool signalOnly) : Mutex(true,"SignallingCall"), m_lastEvent(0), + m_overlap(false), m_controller(controller), m_outgoing(outgoing), m_signalOnly(signalOnly), diff --git a/libs/ysig/yatesig.h b/libs/ysig/yatesig.h index fe0e89d1..eaf618fc 100644 --- a/libs/ysig/yatesig.h +++ b/libs/ysig/yatesig.h @@ -1173,6 +1173,13 @@ public: inline bool signalOnly() const { return m_signalOnly; } + /** + * Check if this call is in overlapped send/recv state + * @return True if this call is expecting more digits to be sent/received + */ + inline bool overlapDialing() const + { return m_overlap; } + /** * Send an event to this call * @param event The event to send @@ -1227,6 +1234,11 @@ protected: */ SignallingEvent* m_lastEvent; + /** + * Call is in overlapped send/recv state + */ + bool m_overlap; + private: SignallingCallControl* m_controller; // Call controller this call belongs to bool m_outgoing; // Call direction @@ -7130,6 +7142,8 @@ private: bool connectCircuit(const char* special = 0); // Transmit the IAM message. Start IAM timer if not started bool transmitIAM(); + // Transmit SAM digits + bool transmitSAM(const char* extra = 0); // Check if the circuit needs continuity testing bool needsTesting(const SS7MsgISUP* msg); // Stop waiting for a SGM (Segmentation) message. Copy parameters to the pending segmented message if sgm is valid. @@ -7142,6 +7156,8 @@ private: inline bool transmitMessage(SS7MsgISUP* msg); // Get the ISUP call controller inline SS7ISUP* isup() const; + // Set overlapped flag. Output a debug message + void setOverlapped(bool on, bool numberComplete = true); State m_state; // Call state SignallingCircuit* m_circuit; // Circuit reserved for this call @@ -7158,6 +7174,9 @@ private: String m_location; // Termination location SS7MsgISUP* m_iamMsg; // Message with the call parameters for outgoing calls SS7MsgISUP* m_sgmMsg; // Pending received message with segmentation flag set + // Overlapped + String m_samDigits; // SAM digits + unsigned int m_sentSamDigits; // The number of sent SAM digits // Timers SignallingTimer m_relTimer; // Send release SignallingTimer m_iamTimer; // Send initial address @@ -7454,6 +7473,7 @@ private: bool m_earlyAcm; // Accept progress/ringing in early ACM bool m_inn; // Routing to internal network number flag int m_defaultSls; // Default SLS to use in outbound calls + unsigned int m_maxCalledDigits; // Maximum digits allowed in Called Number in IAM String m_numPlan; // Numbering plan String m_numType; // Number type String m_numPresentation; // Number presentation @@ -9566,7 +9586,6 @@ private: u_int32_t m_callRefLen; // Call reference length u_int8_t m_tei; // TEI used for the call SignallingCircuit* m_circuit; // Circuit reserved for this call - bool m_overlap; // Call is using overlapped sending bool m_circuitChange; // True if circuit changed bool m_channelIDSent; // Incoming calls: ChannelID IE already sent bool m_rspBearerCaps; // Incoming calls: Send BearerCaps IE in the first response diff --git a/modules/server/ysigchan.cpp b/modules/server/ysigchan.cpp index 5bf0b52e..25edf5e1 100644 --- a/modules/server/ysigchan.cpp +++ b/modules/server/ysigchan.cpp @@ -78,6 +78,7 @@ public: virtual void callAccept(Message& msg); virtual void callRejected(const char* error, const char* reason = 0, const Message* msg = 0); + virtual void connected(const char *reason); virtual void disconnected(bool final, const char* reason); bool disconnect() { return Channel::disconnect(m_reason); } @@ -122,6 +123,10 @@ private: // Process parameter compatibility data from a message // Return true if the call was terminated bool processCompat(const NamedList& list); + // Send a tone through signalling call + bool sendSigTone(Message& msg, const char* tone); + // Release postponed call accepted event. Send it if requested + void releaseCallAccepted(bool send = false); private: String m_caller; String m_called; @@ -133,6 +138,8 @@ private: bool m_rtpForward; // Forward RTP bool m_sdpForward; // Forward SDP (only of rtp forward is enabled) Message* m_route; // Prepared call.preroute message + ObjList m_chanDtmf; // Postponed (received while routing) chan.dtmf messages + SignallingEvent* m_callAccdEvent; // Postponed call accepted event to be sent to remote }; class SigDriver : public Driver @@ -759,7 +766,8 @@ SigChannel::SigChannel(SignallingEvent* event) m_inband(false), m_rtpForward(false), m_sdpForward(false), - m_route(0) + m_route(0), + m_callAccdEvent(0) { if (!(m_call && m_call->ref())) { Debug(this,DebugCall,"No signalling call for this incoming call"); @@ -817,13 +825,15 @@ SigChannel::SigChannel(const char* caller, const char* called) m_inband(false), m_rtpForward(false), m_sdpForward(false), - m_route(0) + m_route(0), + m_callAccdEvent(0) { } SigChannel::~SigChannel() { TelEngine::destruct(m_route); + releaseCallAccepted(); hangup(); setState("destroyed",true,true); } @@ -1062,6 +1072,14 @@ bool SigChannel::msgTone(Message& msg, const char* tone) return true; Lock lock(m_mutex); DDebug(this,DebugCall,"Tone. '%s' %s[%p]",tone,(m_call ? "" : ". No call "),this); + // Outgoing, overlap dialing call: try it first + if (isOutgoing() && m_call && m_call->overlapDialing()) { + lock.drop(); + if (sendSigTone(msg,tone)) + return true; + } + // Re-aquire lock + Lock lock2(m_mutex); // Try to send: through the circuit, in band or through the signalling protocol SignallingCircuit* cic = getCircuit(); if (cic) { @@ -1074,13 +1092,9 @@ bool SigChannel::msgTone(Message& msg, const char* tone) return true; if (!m_call) return true; - SignallingMessage* sm = new SignallingMessage; - sm->params().addParam("tone",tone); - SignallingEvent* event = new SignallingEvent(SignallingEvent::Info,sm,m_call); - TelEngine::destruct(sm); + lock2.drop(); lock.drop(); - plugin.copySigMsgParams(event,msg); - event->sendEvent(); + sendSigTone(msg,tone); return true; } @@ -1158,12 +1172,20 @@ void SigChannel::callAccept(Message& msg) Lock lock(m_mutex); SignallingEvent* event = 0; if (!terminated && m_call) { + // Check if we should accept now the call + bool acceptNow = msg.getBoolValue("accept_call",true); const char* format = msg.getValue("format"); updateConsumer(format,false); SignallingMessage* sm = new SignallingMessage; if (format) sm->params().addParam("format",format); - event = new SignallingEvent(SignallingEvent::Accept,sm,m_call); + if (acceptNow) + event = new SignallingEvent(SignallingEvent::Accept,sm,m_call); + else { + DDebug(this,DebugAll,"Postponing call accepted [%p]",this); + m_callAccdEvent = new SignallingEvent(SignallingEvent::Accept,sm,m_call); + plugin.copySigMsgParams(m_callAccdEvent,msg,"i"); + } TelEngine::destruct(sm); } if (m_rtpForward) { @@ -1178,6 +1200,13 @@ void SigChannel::callAccept(Message& msg) event->sendEvent(); } Channel::callAccept(msg); + // Enqueue pending DTMFs + GenObject* gen = 0; + while (0 != (gen = m_chanDtmf.remove(false))) { + Message* m = static_cast(gen); + complete(*m); + dtmfEnqueue(m); + } } void SigChannel::callRejected(const char* error, const char* reason, const Message* msg) @@ -1188,6 +1217,12 @@ void SigChannel::callRejected(const char* error, const char* reason, const Messa hangup(); } +void SigChannel::connected(const char *reason) +{ + releaseCallAccepted(true); + Channel::connected(m_reason); +} + void SigChannel::disconnected(bool final, const char* reason) { if (m_reason.null()) @@ -1201,6 +1236,7 @@ void SigChannel::hangup(const char* reason, SignallingEvent* event) { static String params = "reason"; Lock lock(m_mutex); + releaseCallAccepted(); if (m_hungup) return; m_hungup = true; @@ -1283,7 +1319,13 @@ void SigChannel::evInfo(SignallingEvent* event) Message* m = message("chan.dtmf"); m->addParam("text",tmp); m->addParam("detected",inband ? "inband" : "signal"); - dtmfEnqueue(m); + m->copyParams(event->message()->params(),"dialing"); + if (status() != "incoming") + dtmfEnqueue(m); + else { + Lock lock(m_mutex); + m_chanDtmf.append(m); + } } } @@ -1506,6 +1548,34 @@ bool SigChannel::processCompat(const NamedList& list) return terminated; } +// Send a tone through signalling call +bool SigChannel::sendSigTone(Message& msg, const char* tone) +{ + Lock lock(m_mutex); + if (!m_call) + return false; + SignallingMessage* sm = new SignallingMessage; + sm->params().addParam("tone",tone); + SignallingEvent* event = new SignallingEvent(SignallingEvent::Info,sm,m_call); + TelEngine::destruct(sm); + lock.drop(); + plugin.copySigMsgParams(event,msg); + return event->sendEvent(); +} + +// Release postponed call accepted event. Sent it if requested +void SigChannel::releaseCallAccepted(bool send) +{ + Lock lock(m_mutex); + if (!m_callAccdEvent) + return; + if (send) + m_callAccdEvent->sendEvent(); + else + delete m_callAccdEvent; + m_callAccdEvent = 0; +} + /** * SigDriver */ diff --git a/share/scripts/overlapped.php b/share/scripts/overlapped.php index a57754e0..2d0010c4 100755 --- a/share/scripts/overlapped.php +++ b/share/scripts/overlapped.php @@ -30,12 +30,21 @@ $partycallid = ""; $state = "call"; $num = ""; $collect = ""; +// Queued, not yet used DTMFs +$queue = ""; +// Initial call.execute message used when re-routing +$executeParams = array(); +// Final digit detected: no more routes allowed +$final = false; +// Don't answer the call, don't use prompts +$routeOnly = true; function setState($newstate) { global $ourcallid; global $state; global $collect; + global $routeOnly; // are we exiting? if ($state == "") @@ -59,6 +68,11 @@ function setState($newstate) if ($newstate == $state) return; + if ($routeOnly) { + $state = $newstate; + return; + } + switch ($newstate) { case "goodbye": $m = new Yate("chan.attach"); @@ -94,8 +108,10 @@ function setState($newstate) function routeTo($num) { global $ourcallid; + global $executeParams; setState("routing"); $m = new Yate("call.route"); + $m->params = $executeParams; $m->params["id"] = $ourcallid; $m->params["called"] = $num; $m->params["overlapped"] = "yes"; @@ -119,25 +135,71 @@ function gotNotify() } } -function gotDTMF($dtmf) +function gotDTMF($dtmfs) { global $state; global $collect; + global $queue; + global $final; + global $routeOnly; - Yate::Debug("Overlapped gotDTMF('$dtmf') in state: $state collected: '$collect'"); - switch ($dtmf) { - case "*": + Yate::Debug("Overlapped gotDTMF('$dtmfs') in state: '$state' collected: '$collect' queued: '$queue'"); + + $queue .= $dtmfs; + if ($state == "routing") + return; + $route = false; + while (true) { + $n = strlen($queue); + if ($n < 1) + break; + if ($state == "call") { + // First call: use all digits to route + $route = true; + if ($queue[$n - 1] != ".") + $collect .= $queue; + else { + $collect .= substr($queue,0,$n - 1); + $final = true; + } + $queue = ""; + break; + } + if ($queue[0] == "*") { + $queue = substr($queue,1); setState(""); - return; - case "#": + continue; + } + if ($queue[0] == "#") { Yate::Output("Overlapped clearing already collected: '$collect'"); - $collect=""; - setState("prompt"); - return; + $collect = ""; + $queue = substr($queue,1); + if (!$routeOnly) + setState("prompt"); + continue; + } + if (!$final) { + $route = true; + $dtmf = $queue[0]; + $queue = substr($queue,1); + $final = ($dtmf == "."); + if (!$final) { + $collect .= $dtmf; + // Check for next char now + $n = strlen($queue); + if ($n > 0 && $queue[0] == ".") { + $final = true; + $queue = substr($queue,1); + } + } + } + break; + } + if ($route) { + if ($final) + Yate::Debug("Overlapped got final digit. Collected: '$collect' queued: '$queue'"); + routeTo($collect); } - - $collect .= $dtmf; - routeTo($collect); } function endRoute($callto,$ok,$err,$params) @@ -145,6 +207,8 @@ function endRoute($callto,$ok,$err,$params) global $partycallid; global $num; global $collect; + global $final; + global $queue; if ($ok && ($callto != "-") && ($callto != "error")) { Yate::Output("Overlapped got route: '$callto' for '$collect'"); $m = new Yate("chan.masquerade"); @@ -155,14 +219,32 @@ function endRoute($callto,$ok,$err,$params) $m->params["caller"] = $num; $m->params["called"] = $collect; $m->Dispatch(); + if (strlen($queue)) { + // Masquerade the remaining digits + // TODO: wait for call.execute to be processed to do that? + $d = new Yate("chan.masquerade"); + $d->params["message"] = "chan.dtmf"; + $d->params["id"] = $partycallid; + $d->params["tone"] = $queue; + $d->Dispatch(); + } return; } - if ($err != "incomplete") { - Yate::Output("Overlapped get error '$err' for '$collect'"); - setState("noroute"); + if ($final) { + Yate::Output("Overlapped got final error '$err' for '$collect'"); + setState(""); } - else + else if ($err != "incomplete") { + Yate::Output("Overlapped got error '$err' for '$collect'"); + setState(""); + $final = true; + } + else { Yate::Debug("Overlapped still incomplete: '$collect'"); + setState("noroute"); + // Check if got some other digits + gotDTMF(""); + } } /* The main loop. We pick events and handle them */ @@ -181,7 +263,17 @@ while ($state != "") { $partycallid = $ev->GetValue("id"); $ev->params["targetid"] = $ourcallid; $num = $ev->GetValue("caller"); - $autoanswer = ($ev->GetValue("called") != "off-hook"); + $routeOnly = !Yate::Str2bool($ev->getValue("accept_call")); + $autoanswer = false; + $callednum = ""; + if ($routeOnly) { + $callednum = $ev->GetValue("called"); + if ($callednum == "off-hook") + $callednum = ""; + } + else + $autoanswer = ($ev->GetValue("called") != "off-hook"); + $executeParams = $ev->params; $ev->handled = true; // we must ACK this message before dispatching a call.answered $ev->Acknowledge(); @@ -193,8 +285,12 @@ while ($state != "") { $m->params["targetid"] = $partycallid; $m->Dispatch(); } + // Route initial called number + if (strlen($callednum)) + gotDTMF($callednum); - setState("prompt"); + if (!$routeOnly) + setState("prompt"); break; case "chan.notify": @@ -206,9 +302,7 @@ while ($state != "") { case "chan.dtmf": if ($ev->GetValue("targetid") == $ourcallid ) { - $dtmfs = $ev->GetValue("text"); - for ($i = 0; $i < strlen($dtmfs); $i++) - gotDTMF($dtmfs[$i]); + gotDTMF($ev->GetValue("text")); $ev->handled = true; } break;