private SIPDialogue Dial( ForkCall call, string data, int ringTimeout, int answeredCallLimit, SIPRequest clientRequest, List <string> customHeaders) { SIPDialogue answeredDialogue = null; ManualResetEvent waitForCallCompleted = new ManualResetEvent(false); //call.CallProgress += (s, r, h, t, b) => { Log("Progress response of " + s + " received on CallBack Dial" + "."); }; call.CallProgress += CallProgress; call.CallFailed += (s, r, h) => { waitForCallCompleted.Set(); }; call.CallAnswered += (s, r, toTag, h, t, b, d, transferMode) => { answeredDialogue = d; waitForCallCompleted.Set(); }; try { Queue <List <SIPCallDescriptor> > callsQueue = m_dialStringParser.ParseDialString(DialPlanContextsEnum.Script, clientRequest, data, customHeaders, null, null, null, null, null, null, null, CustomerServiceLevels.None); call.Start(callsQueue); // Wait for an answer. ringTimeout = (ringTimeout > MAXCALLBACK_RINGTIME_SECONDS || ringTimeout <= 0) ? MAXCALLBACK_RINGTIME_SECONDS : ringTimeout; logger.Debug("Set callback cancel timeout to " + ringTimeout + " seconds."); if (!waitForCallCompleted.WaitOne(ringTimeout * 1000, false)) { call.CancelNotRequiredCallLegs(CallCancelCause.TimedOut); } logger.Debug("Callback dial returning has dialogue ? " + (answeredDialogue == null) + "."); return(answeredDialogue); } catch (Exception excp) { logger.Error("Exception CallbackApp Dial. " + excp); return(null); } }
/// <summary> /// /// </summary> /// <param name="data"></param> /// <param name="ringTimeout"></param> /// <param name="answeredCallLimit"></param> /// <param name="redirectMode"></param> /// <param name="clientTransaction"></param> /// <param name="keepScriptAlive">If false will let the dial plan engine know the script has finished and the call is answered. For applications /// like Callback which need to have two calls answered it will be true.</param> /// <returns></returns> private DialPlanAppResult Dial( string data, int ringTimeout, int answeredCallLimit, SIPRequest clientRequest, CRMHeaders contact) { if (m_dialPlanContext.IsAnswered) { Log("The call has already been answered the Dial command was not processed."); return DialPlanAppResult.AlreadyAnswered; } else if (data.IsNullOrBlank()) { Log("The dial string cannot be empty when calling Dial."); return DialPlanAppResult.Error; } else if (m_callInitialisationCount > MAX_CALLS_ALLOWED) { Log("You have exceeded the maximum allowed calls for a dialplan execution."); return DialPlanAppResult.Error; } else { Log("Commencing Dial with: " + data + "."); DialPlanAppResult result = DialPlanAppResult.Unknown; m_waitForCallCompleted = new ManualResetEvent(false); SIPResponseStatusCodesEnum answeredStatus = SIPResponseStatusCodesEnum.None; string answeredReason = null; string answeredContentType = null; string answeredBody = null; SIPDialogue answeredDialogue = null; SIPDialogueTransferModesEnum uasTransferMode = SIPDialogueTransferModesEnum.Default; int numberLegs = 0; QueueNewCallDelegate queueNewCall = (m_callManager != null) ? m_callManager.QueueNewCall : (QueueNewCallDelegate)null; m_currentCall = new ForkCall(m_sipTransport, FireProxyLogEvent, queueNewCall, m_dialStringParser, Username, m_adminMemberId, m_outboundProxySocket, m_callManager, m_dialPlanContext, out LastDialled); m_currentCall.CallProgress += m_dialPlanContext.CallProgress; m_currentCall.CallFailed += (status, reason, headers) => { LastFailureStatus = status; LastFailureReason = reason; result = DialPlanAppResult.Failed; m_waitForCallCompleted.Set(); }; m_currentCall.CallAnswered += (status, reason, toTag, headers, contentType, body, dialogue, transferMode) => { answeredStatus = status; answeredReason = reason; answeredContentType = contentType; answeredBody = body; answeredDialogue = dialogue; uasTransferMode = transferMode; result = DialPlanAppResult.Answered; m_waitForCallCompleted.Set(); }; try { Queue<List<SIPCallDescriptor>> callsQueue = m_dialStringParser.ParseDialString( DialPlanContextsEnum.Script, clientRequest, data, m_customSIPHeaders, m_customContentType, m_customContent, m_dialPlanContext.CallersNetworkId, m_customFromName, m_customFromUser, m_customFromHost, contact, ServiceLevel); List<SIPCallDescriptor>[] callListArray = callsQueue.ToArray(); callsQueue.ToList().ForEach((list) => numberLegs += list.Count); if (numberLegs == 0) { Log("The dial string did not result in any call legs."); return DialPlanAppResult.Error; } else { m_callInitialisationCount += numberLegs; if (m_callInitialisationCount > MAX_CALLS_ALLOWED) { Log("You have exceeded the maximum allowed calls for a dialplan execution."); return DialPlanAppResult.Error; } } m_currentCall.Start(callsQueue); // Wait for an answer. if (ringTimeout <= 0 || ringTimeout * 1000 > m_maxRingTime) { ringTimeout = m_maxRingTime; } else { ringTimeout = ringTimeout * 1000; } ExtendScriptTimeout(ringTimeout / 1000 + DEFAULT_CREATECALL_RINGTIME); DateTime startTime = DateTime.Now; if (m_waitForCallCompleted.WaitOne(ringTimeout, false)) { if (!m_clientCallCancelled) { if (result == DialPlanAppResult.Answered) { // The call limit duration is only used if there hasn't already been a per leg duration set on the call. if (answeredCallLimit > 0 && answeredDialogue.CallDurationLimit == 0) { answeredDialogue.CallDurationLimit = answeredCallLimit; } m_dialPlanContext.CallAnswered(answeredStatus, answeredReason, null, null, answeredContentType, answeredBody, answeredDialogue, uasTransferMode); // Dial plan script stops once there is an answered call to bridge to or the client call is cancelled. Log("Dial command was successfully answered in " + DateTime.Now.Subtract(startTime).TotalSeconds.ToString("0.00") + "s."); // Do some Google Analytics call tracking. if (answeredDialogue.RemoteUserField != null) { SendGoogleAnalyticsEvent("Call", "Answered", answeredDialogue.RemoteUserField.URI.Host, 1); } m_executingScript.StopExecution(); } else if (result == DialPlanAppResult.Failed) { // Check whether any of the responses were redirects. if (LastDialled != null && LastDialled.Count > 0) { var redirect = (from trans in LastDialled where trans.TransactionFinalResponse != null && trans.TransactionFinalResponse.StatusCode >= 300 && trans.TransactionFinalResponse.StatusCode <= 399 && trans.TransactionFinalResponse.Header.Contact != null && trans.TransactionFinalResponse.Header.Contact.Count > 0 select trans.TransactionFinalResponse).FirstOrDefault(); if (redirect != null) { m_redirectResponse = redirect; m_redirectURI = RedirectResponse.Header.Contact[0].ContactURI; result = DialPlanAppResult.Redirect; } } } } } else { if (!m_clientCallCancelled) { // Call timed out. m_currentCall.CancelNotRequiredCallLegs(CallCancelCause.TimedOut); result = DialPlanAppResult.TimedOut; } } if (m_clientCallCancelled) { Log("Dial command was halted by cancellation of client call after " + DateTime.Now.Subtract(startTime).TotalSeconds.ToString("#.00") + "s."); m_executingScript.StopExecution(); } return result; } catch (ThreadAbortException) { return DialPlanAppResult.Unknown; } catch (Exception excp) { logger.Error("Exception DialPlanScriptFacade Dial. " + excp.Message); return DialPlanAppResult.Error; } } }
/// <summary> /// Processes the matched dial plan command for an outgoing call request. This method is used for "exten =>" formatted dial plans. In addition if the dial /// plan owner has requested that their dialplan be used for incoming calls it will process those as well. /// </summary> /// <param name="localEndPoint">The SIP Proxy socket the request was received on.</param> /// <param name="remoteEndPoint">The socket the request was recevied from.</param> /// <param name="transaction">The SIP Invite transaction that initiated the dial plan processing.</param> /// <param name="manglePrivateAddresses">If true private IP addresses will be subtituted for the remote socket.</param> /// <param name="canonicalFromDomain">If (and only if) the call is an outgoing call this will be set to the canonical domain of the host in the SIP From /// header. An outgoing call is one from an authenticated user destined for an external SIP URI. If the call is an incoming this will be null.</param> /// <param name="canonicalToDomain">If (and only if) the call is an incoming call this will be set to the canonical domain of the host in the SIP URI /// request. An incoming call is one from an external caller to a URI corresponding to a hosted domain on this SIP Proxy.</param> private void ExecuteDialPlanLine( DialPlanLineContext dialPlanContext, ISIPServerUserAgent uas, SIPCallDirection callDirection, DialogueBridgeCreatedDelegate createBridgeDelegate, ISIPCallManager callManager) { try { //SIPRequest sipRequest = uas.CallRequest; FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Executing line dial plan for call to " + uas.CallDestination + ".", dialPlanContext.Owner)); DialPlanCommand matchedCommand = dialPlanContext.GetDialPlanMatch(uas.CallDestination); if (matchedCommand == null) { FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Destination " + uas.CallDestination + " not found in line dial plan " + dialPlanContext.SIPDialPlan.DialPlanName + ".", dialPlanContext.Owner)); dialPlanContext.CallFailed(SIPResponseStatusCodesEnum.NotFound, null, null); } else if (Regex.Match(matchedCommand.Command, "Switch|Dial", RegexOptions.IgnoreCase).Success) { if (matchedCommand.Data != null && matchedCommand.Data.Trim().Length > 0) { DialStringParser dialStringParser = new DialStringParser(m_sipTransport, dialPlanContext.Owner, dialPlanContext.SIPAccount, dialPlanContext.SIPProviders, m_sipSorceryPersistor.SIPAccountsPersistor.Get, m_sipSorceryPersistor.SIPRegistrarBindingPersistor.Get, GetCanonicalDomainDelegate_External, LogDelegate_External, dialPlanContext.SIPDialPlan.DialPlanName); ForkCall ForkCall = new ForkCall(m_sipTransport, FireProxyLogEvent, callManager.QueueNewCall, dialStringParser, dialPlanContext.Owner, dialPlanContext.AdminMemberId, m_outboundProxySocket, null, null); ForkCall.CallProgress += dialPlanContext.CallProgress; ForkCall.CallFailed += dialPlanContext.CallFailed; ForkCall.CallAnswered += dialPlanContext.CallAnswered; Queue<List<SIPCallDescriptor>> calls = dialStringParser.ParseDialString(DialPlanContextsEnum.Line, uas.CallRequest.Copy(), matchedCommand.Data, null, null, null, dialPlanContext.CallersNetworkId, null, null, null, null, CustomerServiceLevels.None); ForkCall.Start(calls); } else { FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Error processing dialplan Dial command the dial string was empty.", dialPlanContext.Owner)); } } //else if (Regex.Match(matchedCommand.Command, "RTSP", RegexOptions.IgnoreCase).Success) //{ // RTSPApp rtspCall = new RTSPApp(FireProxyLogEvent, (UASInviteTransaction)transaction, dialPlanContext.Owner); // rtspCall.Start(matchedCommand.Data); //} else if (Regex.Match(matchedCommand.Command, "SIPReply", RegexOptions.IgnoreCase).Success) { string[] replyFields = matchedCommand.Data.Split(','); string statusMessage = (replyFields.Length > 1 && replyFields[1] != null) ? replyFields[1].Trim() : null; SIPResponseStatusCodesEnum status = SIPResponseStatusCodes.GetStatusTypeForCode(Convert.ToInt32(replyFields[0])); if ((int)status >= 300) { dialPlanContext.CallFailed(status, statusMessage, null); } else if ((int)status < 200) { dialPlanContext.CallProgress(status, statusMessage, null, null, null, null); } } else { FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.Error, "Command " + matchedCommand.Command + " is not a valid dial plan command.", dialPlanContext.Owner)); dialPlanContext.CallFailed(SIPResponseStatusCodesEnum.InternalServerError, "Invalid dialplan command " + matchedCommand.Command, null); } } catch (Exception excp) { FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.Error, "Error executing line dialplan for " + uas.CallRequest.URI.ToString() + ". " + excp.Message, dialPlanContext.Owner)); dialPlanContext.DialPlanExecutionFinished(); } }
private SIPDialogue Dial( string data, int ringTimeout, int answeredCallLimit, SIPRequest clientRequest, List<string> customHeaders) { SIPDialogue answeredDialogue = null; ManualResetEvent waitForCallCompleted = new ManualResetEvent(false); ForkCall call = new ForkCall(m_sipTransport, Log_External, m_callManager.QueueNewCall, null, m_username, m_adminMemberId, m_outboundProxy, m_callManager, null); //call.CallProgress += (s, r, h, t, b) => { Log("Progress response of " + s + " received on CallBack Dial" + "."); }; call.CallProgress += CallProgress; call.CallFailed += (s, r, h) => { waitForCallCompleted.Set(); }; call.CallAnswered += (s, r, toTag, h, t, b, d, transferMode) => { answeredDialogue = d; waitForCallCompleted.Set(); }; try { Queue<List<SIPCallDescriptor>> callsQueue = m_dialStringParser.ParseDialString(DialPlanContextsEnum.Script, clientRequest, data, customHeaders, null, null, null, null, null, null, null, CustomerServiceLevels.None); call.Start(callsQueue); // Wait for an answer. ringTimeout = (ringTimeout > m_maxRingTime) ? m_maxRingTime : ringTimeout; if (waitForCallCompleted.WaitOne(ringTimeout * 1000, false)) { // Call timed out. call.CancelNotRequiredCallLegs(CallCancelCause.TimedOut); } return answeredDialogue; } catch (Exception excp) { logger.Error("Exception CallbackApp Dial. " + excp); return null; } }
private SIPDialogue Dial( ForkCall call, string data, int ringTimeout, int answeredCallLimit, SIPRequest clientRequest, List<string> customHeaders) { SIPDialogue answeredDialogue = null; ManualResetEvent waitForCallCompleted = new ManualResetEvent(false); //call.CallProgress += (s, r, h, t, b) => { Log("Progress response of " + s + " received on CallBack Dial" + "."); }; call.CallProgress += CallProgress; call.CallFailed += (s, r, h) => { waitForCallCompleted.Set(); }; call.CallAnswered += (s, r, toTag, h, t, b, d, transferMode) => { answeredDialogue = d; waitForCallCompleted.Set(); }; try { Queue<List<SIPCallDescriptor>> callsQueue = m_dialStringParser.ParseDialString(DialPlanContextsEnum.Script, clientRequest, data, customHeaders, null, null, null, null, null, null, null, CustomerServiceLevels.None); call.Start(callsQueue); // Wait for an answer. ringTimeout = (ringTimeout > MAXCALLBACK_RINGTIME_SECONDS || ringTimeout <= 0) ? MAXCALLBACK_RINGTIME_SECONDS : ringTimeout; logger.Debug("Set callback cancel timeout to " + ringTimeout + " seconds."); if (!waitForCallCompleted.WaitOne(ringTimeout * 1000, false)) { call.CancelNotRequiredCallLegs(CallCancelCause.TimedOut); } logger.Debug("Callback dial returning has dialogue ? " + (answeredDialogue == null) + "."); return answeredDialogue; } catch (Exception excp) { logger.Error("Exception CallbackApp Dial. " + excp); return null; } }
/// <summary> /// Processes the matched dial plan command for an outgoing call request. This method is used for "exten =>" formatted dial plans. In addition if the dial /// plan owner has requested that their dialplan be used for incoming calls it will process those as well. /// </summary> /// <param name="localEndPoint">The SIP Proxy socket the request was received on.</param> /// <param name="remoteEndPoint">The socket the request was recevied from.</param> /// <param name="transaction">The SIP Invite transaction that initiated the dial plan processing.</param> /// <param name="manglePrivateAddresses">If true private IP addresses will be subtituted for the remote socket.</param> /// <param name="canonicalFromDomain">If (and only if) the call is an outgoing call this will be set to the canonical domain of the host in the SIP From /// header. An outgoing call is one from an authenticated user destined for an external SIP URI. If the call is an incoming this will be null.</param> /// <param name="canonicalToDomain">If (and only if) the call is an incoming call this will be set to the canonical domain of the host in the SIP URI /// request. An incoming call is one from an external caller to a URI corresponding to a hosted domain on this SIP Proxy.</param> private void ExecuteDialPlanLine( DialPlanLineContext dialPlanContext, ISIPServerUserAgent uas, SIPCallDirection callDirection, DialogueBridgeCreatedDelegate createBridgeDelegate, ISIPCallManager callManager) { try { //SIPRequest sipRequest = uas.CallRequest; FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Executing line dial plan for call to " + uas.CallDestination + ".", dialPlanContext.Owner)); DialPlanCommand matchedCommand = dialPlanContext.GetDialPlanMatch(uas.CallDestination); if (matchedCommand == null) { FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Destination " + uas.CallDestination + " not found in line dial plan " + dialPlanContext.SIPDialPlan.DialPlanName + ".", dialPlanContext.Owner)); dialPlanContext.CallFailed(SIPResponseStatusCodesEnum.NotFound, null, null); } else if (Regex.Match(matchedCommand.Command, "Switch|Dial", RegexOptions.IgnoreCase).Success) { if (matchedCommand.Data != null && matchedCommand.Data.Trim().Length > 0) { DialStringParser dialStringParser = new DialStringParser(m_sipTransport, dialPlanContext.Owner, dialPlanContext.SIPAccount, dialPlanContext.SIPProviders, m_sipSorceryPersistor.SIPAccountsPersistor.Get, m_sipSorceryPersistor.SIPRegistrarBindingPersistor.Get, GetCanonicalDomainDelegate_External, LogDelegate_External, dialPlanContext.SIPDialPlan.DialPlanName); ForkCall ForkCall = new ForkCall(m_sipTransport, FireProxyLogEvent, callManager.QueueNewCall, dialStringParser, dialPlanContext.Owner, dialPlanContext.AdminMemberId, m_outboundProxySocket, null, null); ForkCall.CallProgress += dialPlanContext.CallProgress; ForkCall.CallFailed += dialPlanContext.CallFailed; ForkCall.CallAnswered += dialPlanContext.CallAnswered; Queue <List <SIPCallDescriptor> > calls = dialStringParser.ParseDialString(DialPlanContextsEnum.Line, uas.CallRequest.Copy(), matchedCommand.Data, null, null, null, dialPlanContext.CallersNetworkId, null, null, null, null, CustomerServiceLevels.None); ForkCall.Start(calls); } else { FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Error processing dialplan Dial command the dial string was empty.", dialPlanContext.Owner)); } } //else if (Regex.Match(matchedCommand.Command, "RTSP", RegexOptions.IgnoreCase).Success) //{ // RTSPApp rtspCall = new RTSPApp(FireProxyLogEvent, (UASInviteTransaction)transaction, dialPlanContext.Owner); // rtspCall.Start(matchedCommand.Data); //} else if (Regex.Match(matchedCommand.Command, "SIPReply", RegexOptions.IgnoreCase).Success) { string[] replyFields = matchedCommand.Data.Split(','); string statusMessage = (replyFields.Length > 1 && replyFields[1] != null) ? replyFields[1].Trim() : null; SIPResponseStatusCodesEnum status = SIPResponseStatusCodes.GetStatusTypeForCode(Convert.ToInt32(replyFields[0])); if ((int)status >= 300) { dialPlanContext.CallFailed(status, statusMessage, null); } else if ((int)status < 200) { dialPlanContext.CallProgress(status, statusMessage, null, null, null, null); } } else { FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.Error, "Command " + matchedCommand.Command + " is not a valid dial plan command.", dialPlanContext.Owner)); dialPlanContext.CallFailed(SIPResponseStatusCodesEnum.InternalServerError, "Invalid dialplan command " + matchedCommand.Command, null); } } catch (Exception excp) { FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.Error, "Error executing line dialplan for " + uas.CallRequest.URI.ToString() + ". " + excp.Message, dialPlanContext.Owner)); dialPlanContext.DialPlanExecutionFinished(); } }