/// <summary> /// Processes a dialplan script (currently Ruby scripts only) for a received SIP INVITE request. /// </summary> /// <param name="coreLogDelegate">A function delegate that passes log/diagnostics events back to the SIP Proxy Core.</param> /// <param name="createBridgeDelegate">A function delegate that is called in the event that the dial plan command results in a call being answered and a bridge needing to be created.</param> /// <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="clientTransaction">The SIP Invite transaction that initiated the dial plan processing.</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 ExecuteDialPlanScript( DialPlanScriptContext dialPlanContext, ISIPServerUserAgent uas, SIPCallDirection callDirection, DialogueBridgeCreatedDelegate createBridgeDelegate, ISIPCallManager callManager) { try { if (uas == null) { throw new ArgumentNullException("The ISIPServerUserAgent parameter cannot be null when attempting to execute a dialplan script."); } FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.NewCall, "Executing script dial plan for call to " + uas.CallDestination + ".", dialPlanContext.Owner)); if (!dialPlanContext.DialPlanScript.IsNullOrBlank()) { DialPlanExecutingScript dialPlanExecutionScript = null; int runningScriptCount = (from script in m_runningScripts where !script.Complete select script).Count(); if (runningScriptCount < MAX_ALLOWED_SCRIPTSCOPES) { m_dialPlanScriptContextsCreated++; //FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Creating DialPlanExecutingScript number " + m_dialPlanScriptContextsCreated + " for dialplan execution for script owned by " + dialPlanContext.Owner + ".", null)); dialPlanExecutionScript = new DialPlanExecutingScript(FireProxyLogEvent); } else { FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Running script limit of " + MAX_ALLOWED_SCRIPTSCOPES + " reached.", null)); lock (m_runningScripts) { foreach (DialPlanExecutingScript runningScript in m_runningScripts) { FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, " running script owner=" + runningScript.Owner + ", dialplan name=" + runningScript.ExecutingDialPlanContext.SIPDialPlan.DialPlanName + ", start time=" + runningScript.StartTime.ToString("dd MMM yyyy HH:mm:ss") + ", is complete=" + runningScript.Complete + ".", null)); } } } if (dialPlanExecutionScript != null) { dialPlanExecutionScript.Initialise(dialPlanContext); DialPlanScriptFacade planFacade = new DialPlanScriptFacade( m_sipTransport, dialPlanExecutionScript, FireProxyLogEvent, createBridgeDelegate, (uas.CallRequest != null) ? uas.CallRequest.Copy() : null, // A different copy to the req object. Stops inadvertent changes in the dialplan. callDirection, dialPlanContext, GetCanonicalDomainDelegate_External, callManager, m_sipSorceryPersistor, m_outboundProxySocket, this); DialPlanCRMFacade crmFacade = new DialPlanCRMFacade(FireProxyLogEvent, dialPlanContext); DialPlanLookupFacade lookupFacade = new DialPlanLookupFacade(FireProxyLogEvent, dialPlanContext.Owner); ScriptScope rubyScope = dialPlanExecutionScript.DialPlanScriptScope; rubyScope.SetVariable(SCRIPT_HELPEROBJECT_NAME, planFacade); rubyScope.SetVariable(SCRIPT_CRMOBJECT_NAME, crmFacade); rubyScope.SetVariable(SCRIPT_LOOKUPOBJECT_NAME, lookupFacade); if (uas.CallRequest != null) { rubyScope.SetVariable(SCRIPT_REQUESTOBJECT_NAME, uas.CallRequest.Copy()); } dialPlanExecutionScript.DialPlanScriptThread = new Thread(new ParameterizedThreadStart(delegate { ExecuteScript(dialPlanExecutionScript, dialPlanContext, planFacade, m_rubyScriptCommon + dialPlanContext.DialPlanScript); })); lock (m_runningScripts) { m_runningScripts.Add(dialPlanExecutionScript); } dialPlanExecutionScript.DialPlanScriptThread.Start(); } else { FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.Error, "Error processing call " + uas.CallDestination + " there were no script slots available, script could not be executed.", dialPlanContext.Owner)); dialPlanContext.CallFailed(SIPResponseStatusCodesEnum.InternalServerError, "Dial plan script engine was overloaded", null); } } else { FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "A script dial plan was empty, execution cannot continue.", dialPlanContext.Owner)); dialPlanContext.CallFailed(SIPResponseStatusCodesEnum.InternalServerError, "Dial plan script was empty", null); } } catch (Exception excp) { FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.Error, "Error executing script dialplan for " + uas.CallDestination + ". " + excp.Message, dialPlanContext.Owner)); dialPlanContext.CallFailed(SIPResponseStatusCodesEnum.InternalServerError, "Dial plan exception starting script", null); } }