private void ProcessNewCall(ISIPServerUserAgent uas) { //bool wasExecutionCountIncremented = false; Customer customer = null; SIPDialPlan dialPlan = null; try { Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Call Manager processing new call on thread " + Thread.CurrentThread.Name + " for " + uas.CallRequest.Method + " to " + uas.CallRequest.URI.ToString() + ".", null)); #region Do some pre-flight checks on the SIP account to determine if the call should be processed. if (uas.SIPAccount == null) { if (uas.CallRequest.URI.User == DISPATCHER_SIPACCOUNT_NAME) { // This is a call from the monitoring system allow to proceed. } else if (uas.CallDirection == SIPCallDirection.Out) { Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "SIP account " + uas.CallRequest.Header.From.FromURI.ToParameterlessString() + " not found for outgoing call to " + uas.CallRequest.URI.ToString() + ".", null)); uas.Reject(SIPResponseStatusCodesEnum.Forbidden, null, null); return; } else if (uas.CallDirection == SIPCallDirection.In) { Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "SIP account " + uas.CallRequest.URI.ToParameterlessString() + " not found for incoming call.", null)); uas.Reject(SIPResponseStatusCodesEnum.NotFound, null, null); return; } } else { if (uas.SIPAccount.IsDisabled) { Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "SIP account " + uas.SIPAccount.SIPUsername + "@" + uas.SIPAccount.SIPDomain + " is disabled for " + uas.CallDirection + " call.", uas.SIPAccount.Owner)); uas.Reject(SIPResponseStatusCodesEnum.Forbidden, "SIP account disabled", null); return; } else if (uas.SIPAccount.IsIncomingOnly && uas.CallDirection == SIPCallDirection.Out) { Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "SIP account " + uas.SIPAccount.SIPUsername + "@" + uas.SIPAccount.SIPDomain + " is not permitted to make outgoing calls", uas.SIPAccount.Owner)); uas.Reject(SIPResponseStatusCodesEnum.Forbidden, "SIP account not permitted to make outgoing calls", null); return; } } #endregion SIPURI callURI = (uas.CallRequest != null) ? uas.CallRequest.URI : null; SIPAccount sipAccount = uas.SIPAccount; if (uas.CallDirection == SIPCallDirection.In && callURI.User == DISPATCHER_SIPACCOUNT_NAME) { uas.NoCDR(); #region Create a pseudo-dialplan to process the monitoring process call. string pseudoScript = //"sys.Log(\"Dispatcher Call.\")\n" + "result = sys.DoesSIPAccountExist(\"" + DISPATCHER_SIPACCOUNT_NAME + "\")\n" + // Allows the test call to check the database connectivity. //"sys.Log(\"DoesSIPAccountExist result=#{result}.\")\n" + "sys.Respond(420, nil, \"DialPlanEngine-ExecutionCount: " + m_dialPlanEngine.ScriptCount + "\")\n"; SIPDialPlan dispatcherDialPlan = new SIPDialPlan(null, null, null, pseudoScript, SIPDialPlanScriptTypesEnum.Ruby); dispatcherDialPlan.Id = Guid.Empty; // Prevents the increment and decrement on the execution counts. DialPlanScriptContext scriptContext = new DialPlanScriptContext( Log_External, m_sipTransport, CreateDialogueBridge, m_outboundProxy, uas, dispatcherDialPlan, null, null, null, null, null, null); m_dialPlanEngine.Execute(scriptContext, uas, uas.CallDirection, null, this); #endregion } else { string dialPlanName = (uas.CallDirection == SIPCallDirection.Out) ? sipAccount.OutDialPlanName : sipAccount.InDialPlanName; string owner = (uas.IsB2B) ? uas.SIPAccount.Owner : uas.Owner; if (GetDialPlanAndCustomer(owner, dialPlanName, uas, out customer, out dialPlan)) { //IncrementDialPlanExecutionCount(dialPlan, customer, originalExecutionCount + 1); //IncrementCustomerExecutionCount(customer); //wasExecutionCountIncremented = true; if (dialPlan != null) { if (!uas.IsInvite && !dialPlan.AcceptNonInvite) { Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "A " + uas.CallRequest.Method + " was rejected for dialplan " + dialPlanName + " as it does not accept non-INVITE requests.", owner)); uas.Reject(SIPResponseStatusCodesEnum.MethodNotAllowed, "User has chosen not to accept request type", null); return; } else { Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Using dialplan " + dialPlanName + " for " + uas.CallDirection + " call to " + callURI.ToString() + ".", owner)); if (dialPlan.ScriptType == SIPDialPlanScriptTypesEnum.Asterisk) { DialPlanLineContext lineContext = new DialPlanLineContext( Log_External, m_sipTransport, CreateDialogueBridge, m_outboundProxy, uas, dialPlan, GetSIPProviders_External(p => p.Owner == owner, null, 0, Int32.MaxValue), m_traceDirectory, (uas.CallDirection == SIPCallDirection.Out) ? sipAccount.NetworkId : null, customer); //lineContext.DialPlanComplete += () => { DecrementCustomerExecutionCount(customer);} ; m_dialPlanEngine.Execute(lineContext, uas, uas.CallDirection, CreateDialogueBridge, this); } else { dialPlan.AuthorisedApps = customer.AuthorisedApps + ";" + dialPlan.AuthorisedApps; DialPlanScriptContext scriptContext = new DialPlanScriptContext( Log_External, m_sipTransport, CreateDialogueBridge, m_outboundProxy, uas, dialPlan, GetSIPProviders_External(p => p.Owner == owner, null, 0, Int32.MaxValue), m_traceDirectory, (uas.CallDirection == SIPCallDirection.Out) ? sipAccount.NetworkId : null, customer, m_dialPlanEngine, GetCanonicalDomain_External); uas.SetDialPlanContextID(scriptContext.DialPlanContextID); //scriptContext.DialPlanComplete += () => { DecrementCustomerExecutionCount(customer);}; m_dialPlanEngine.Execute(scriptContext, uas, uas.CallDirection, CreateDialogueBridge, this); } } } else if (uas.CallDirection == SIPCallDirection.In) { if (uas.IsB2B) { Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Dialplan could not be loaded for incoming B2B call to " + callURI.ToString() + ".", owner)); uas.Reject(SIPResponseStatusCodesEnum.InternalServerError, "Error loading incoming dial plan for B2B call", null); //DecrementCustomerExecutionCount(customer); } else { Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "No dialplan specified for incoming call to " + sipAccount.SIPUsername + "@" + sipAccount.SIPDomain + ", registered bindings will be used.", owner)); // The SIP account has no dialplan for an incoming call therefore send to the SIP account's bindings. List<SIPRegistrarBinding> bindings = GetSIPAccountBindings_External(b => b.SIPAccountId == sipAccount.Id, null, 0, MAX_FORWARD_BINDINGS); if (bindings != null && bindings.Count > 0) { // Create a pseudo-dialplan to process the incoming call. string pseudoScript = "sys.Dial(\"" + sipAccount.SIPUsername + "@" + sipAccount.SIPDomain + "\")\n"; Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Forwarding incoming call for " + sipAccount.SIPUsername + "@" + sipAccount.SIPDomain + " to " + bindings.Count + " bindings.", owner)); SIPDialPlan incomingDialPlan = new SIPDialPlan(sipAccount.Owner, null, null, pseudoScript, SIPDialPlanScriptTypesEnum.Ruby); incomingDialPlan.Id = Guid.Empty; // Prevents the increment and decrement on the execution counts. DialPlanScriptContext scriptContext = new DialPlanScriptContext( Log_External, m_sipTransport, CreateDialogueBridge, m_outboundProxy, uas, incomingDialPlan, null, m_traceDirectory, null, customer, null, GetCanonicalDomain_External); //scriptContext.DialPlanComplete += () => { DecrementCustomerExecutionCount(customer); }; m_dialPlanEngine.Execute(scriptContext, uas, uas.CallDirection, CreateDialogueBridge, this); } else { Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "No bindings available for " + sipAccount.SIPUsername + "@" + sipAccount.SIPDomain + " returning temporarily not available.", owner)); uas.Reject(SIPResponseStatusCodesEnum.TemporarilyUnavailable, null, null); //DecrementDialPlanExecutionCount(null, customer.Id); //DecrementCustomerExecutionCount(customer); } } } else { // Couldn't load a dialplan for an outgoing call. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Dialplan could not be loaded for " + uas.CallDirection + " call to " + callURI.ToString() + ".", owner)); uas.Reject(SIPResponseStatusCodesEnum.InternalServerError, "Error loading dial plan", null); //DecrementDialPlanExecutionCount(null, customer.Id); //DecrementCustomerExecutionCount(customer); } } } } catch (Exception excp) { Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.Error, "Exception SIPCallManager ProcessNewCall. " + excp.Message, null)); uas.Reject(SIPResponseStatusCodesEnum.InternalServerError, "Exception ProcessNewCall", null); //if (wasExecutionCountIncremented) //{ //DecrementDialPlanExecutionCount(dialPlan, customer.Id); // DecrementCustomerExecutionCount(customer); //} } }
/// <summary> /// Processes actions initiated by the callmanager web service EXCEPT for the callback method. /// </summary> /// <param name="username">The UNAUTHENTICATED username that was specified in the callmanager request URL.</param> /// <param name="number">The number parameter that was specified in the callmanager request URL.</param> /// <param name="dialplanName">The dialplan to use to process web calls, typically this will be ahrd coded to a known dialplan name.</param> /// <param name="replacesCallID">The replacesCallID parameter that was specified in the callmanager request URL.</param> /// <returns>A string that is returned to the user making the callmanager request that inidcates what action was taken.</returns> public string ProcessWebCall(string username, string number, string dialplanName, string replacesCallID) { //bool wasExecutionCountIncremented = false; Customer customer = null; SIPDialPlan dialPlan = null; try { customer = m_customerPersistor.Get(c => c.CustomerUsername == username); SIPDialogue replacesDialogue = (!replacesCallID.IsNullOrBlank() && customer != null) ? m_sipDialogueManager.GetDialogueRelaxed(customer.CustomerUsername, replacesCallID) : null; if (customer == null) { Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Web " + dialplanName + " rejected for " + username + " and " + number + ", as no matching user.", null)); return "Sorry no matching user was found, the " + dialplanName + " was not initiated."; } else if (customer.Suspended) { Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Web " + dialplanName + " rejected for " + username + " and " + number + ", user account is suspended.", null)); return "Sorry the user's account is suspended."; } else if (customer.ServiceLevel == CustomerServiceLevels.PremiumPayReqd.ToString() || customer.ServiceLevel == CustomerServiceLevels.ProfessionalPayReqd.ToString() || customer.ServiceLevel == CustomerServiceLevels.SwitchboardPayReqd.ToString()) { Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Web " + dialplanName + " rejected for " + username + " and " + number + ", user account requires payment.", null)); return "Sorry the user's account requires payment."; } else if (!replacesCallID.IsNullOrBlank() && replacesDialogue == null) { return "Sorry the blind transfer could not be initiated, the Call-ID to transfer could not be found."; } else { dialPlan = GetDialPlan_External(d => d.Owner == username && d.DialPlanName == dialplanName); if (dialPlan == null) { Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Web " + dialplanName + " rejected as no " + dialplanName + " dialplan exists.", username)); return "Sorry the specified user has not enabled callbacks, the callback was not initiated."; } else { if (!IsDialPlanExecutionAllowed(dialPlan, customer)) { Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Execution of web call for dialplan " + dialplanName + " was not processed as maximum execution count has been reached.", username)); return "Sorry the callback was not initiated, dial plan execution exceeded maximum allowed"; } else { //IncrementDialPlanExecutionCount(dialPlan, customer, originalExecutionCount + 1); //IncrementCustomerExecutionCount(customer); Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Web call for " + dialplanName + " initialising to " + number + ".", username)); ISIPServerUserAgent uas = null; if (replacesCallID.IsNullOrBlank()) { UASInviteTransaction dummyTransaction = GetDummyWebCallbackTransaction(number); uas = new SIPServerUserAgent(m_sipTransport, m_outboundProxy, username, SIPDomainManager.DEFAULT_LOCAL_DOMAIN, SIPCallDirection.Out, GetSIPAccount_External, null, Log_External, dummyTransaction); } else { SIPDialogue oppositeDialogue = m_sipDialogueManager.GetOppositeDialogue(replacesDialogue); uas = new SIPTransferServerUserAgent(Log_External, m_sipDialogueManager.DialogueTransfer, m_sipTransport, m_outboundProxy, replacesDialogue, oppositeDialogue, number, customer.CustomerUsername, customer.AdminId); m_inProgressTransfers.Add(oppositeDialogue, uas as SIPTransferServerUserAgent); } dialPlan.AuthorisedApps = customer.AuthorisedApps + ";" + dialPlan.AuthorisedApps; DialPlanScriptContext scriptContext = new DialPlanScriptContext( Log_External, m_sipTransport, CreateDialogueBridge, m_outboundProxy, uas, dialPlan, GetSIPProviders_External(p => p.Owner == username, null, 0, Int32.MaxValue), m_traceDirectory, null, customer, null, GetCanonicalDomain_External); //scriptContext.DialPlanComplete += () => { DecrementCustomerExecutionCount(customer); }; m_dialPlanEngine.Execute(scriptContext, uas, SIPCallDirection.Out, CreateDialogueBridge, this); if (replacesCallID.IsNullOrBlank()) { return "Web call was successfully initiated."; } else { return "Blind transfer was successfully initiated."; } } } } } catch (Exception excp) { logger.Error("Exception SIPCallManager ProcessWebCall. " + excp.Message); //if (wasExecutionCountIncremented) //{ //DecrementDialPlanExecutionCount(dialPlan, customer.Id, originalExecutionCount); //DecrementCustomerExecutionCount(customer); //} return "Sorry there was an unexpected error, the callback was not initiated."; } }
/// <summary> /// Processes the callback action that is initiated by the callmanager service. The callback method is typically initiated from /// a link on an authenticated web page and requires the user to be authenticated. /// </summary> /// <param name="username">The authenticated username of the user making the callback request.</param> /// <param name="dialString1">The first leg dial string of the callback.</param> /// <param name="dialString2">The second leg dial string of the callback.</param> /// <returns>A string that is returned to the user making the callmanager request that inidcates what action was taken.</returns> public string ProcessCallback(string username, string dialString1, string dialString2) { Customer customer = null; try { customer = m_customerPersistor.Get(c => c.CustomerUsername == username); if (customer == null) { Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Callback rejected for " + username + " as no matching user.", null)); return "Sorry no matching user was found, the callback was not initiated."; } else if (customer.Suspended) { Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Callback rejected for " + username + " as user account is suspended.", null)); return "Sorry your account is suspended."; } else if (customer.ServiceLevel == CustomerServiceLevels.PremiumPayReqd.ToString() || customer.ServiceLevel == CustomerServiceLevels.ProfessionalPayReqd.ToString() || customer.ServiceLevel == CustomerServiceLevels.SwitchboardPayReqd.ToString()) { Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Callback rejected for " + username + ", user account requires payment.", null)); return "Sorry your account requires payment."; } else { UASInviteTransaction dummyTransaction = GetDummyWebCallbackTransaction("callback"); ISIPServerUserAgent uas = new SIPServerUserAgent(m_sipTransport, m_outboundProxy, username, SIPDomainManager.DEFAULT_LOCAL_DOMAIN, SIPCallDirection.Out, GetSIPAccount_External, null, Log_External, dummyTransaction); string callbackScript = "sys.Log(\"Callback dialString1=" + dialString1 + ", dialString2=" + dialString2 + ".\")\n" + "sys.Callback(\"" + dialString1 + "\",\"" + dialString2 + "\", 0)\n"; SIPDialPlan callbackDialPlan = new SIPDialPlan(username, null, null, callbackScript, SIPDialPlanScriptTypesEnum.Ruby); callbackDialPlan.Id = Guid.Empty; // Prevents the increment and decrement on the execution counts. DialPlanScriptContext scriptContext = new DialPlanScriptContext( Log_External, m_sipTransport, CreateDialogueBridge, m_outboundProxy, uas, callbackDialPlan, GetSIPProviders_External(p => p.Owner == username, null, 0, Int32.MaxValue), null, null, customer, null, GetCanonicalDomain_External); m_dialPlanEngine.Execute(scriptContext, uas, SIPCallDirection.Out, CreateDialogueBridge, this); return null; } } catch (Exception excp) { logger.Error("Exception SIPCallManager ProcessCallback. " + excp.Message); return "Sorry there was an unexpected error, the callback was not initiated."; } }
/// <summary> /// Processes an in dialogue REFER request that specifies a new destination for an existing call leg. /// </summary> /// <param name="username">The username of the user the transfer is being processed for.</param> /// <param name="referTo">The Refer-To header URI from the REFER request.</param> /// <param name="dialplanName">The dialplan to use to process the transfer.</param> /// <param name="replacesCallID">The call ID that is being replaced by the new dialogue if one is created.</param> /// <returns>A SIP server user agent.</returns> public ISIPServerUserAgent BlindTransfer(string username, SIPURI referTo, string dialplanName, SIPDialogue replacesDialogue) { if (dialplanName.IsNullOrBlank()) { throw new ApplicationException("A dial plan name must be provided when processing a blind transfer."); } else if (referTo == null) { throw new ApplicationException("The refer to URI cannot be empty when processing a blind transfer."); } else if (replacesDialogue == null) { throw new ApplicationException("The blind transfer could not be initiated, the dialogue to transfer could not be found."); } //bool wasExecutionCountIncremented = false; Customer customer = null; SIPDialPlan dialPlan = null; try { customer = m_customerPersistor.Get(c => c.CustomerUsername == username); if (customer == null) { Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Blind transfer using dialplan " + dialplanName + " rejected for " + username + " and " + referTo.ToString() + ", as no matching user.", username)); throw new ApplicationException("No matching user was found, the blind transfer was not initiated."); } else if (customer.Suspended) { Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Blind transfer using dialplan " + dialplanName + " rejected for " + username + " and " + referTo.ToString() + ", user account is suspended.", username)); throw new ApplicationException("The user's account is suspended, the blind transfer was not initiated."); } else { dialPlan = GetDialPlan_External(d => d.Owner == username && d.DialPlanName == dialplanName); if (dialPlan == null) { Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Blind transfer rejected as no " + dialplanName + " dialplan exists.", username)); throw new ApplicationException("The blind transfer could not be initiated, no dialplan with name " + dialplanName + " could be found."); } else { if (!IsDialPlanExecutionAllowed(dialPlan, customer)) { Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Execution of blind transfer for dialplan " + dialplanName + " was not processed as maximum execution count has been reached.", username)); throw new ApplicationException("The blind transfer was not initiated, dial plan execution exceeded maximum allowed"); } else { //IncrementCustomerExecutionCount(customer); Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Blind transfer for dialplan " + dialplanName + " starting for " + referTo.ToString() + ".", username)); SIPDialogue oppositeDialogue = m_sipDialogueManager.GetOppositeDialogue(replacesDialogue); ISIPServerUserAgent uas = new SIPTransferServerUserAgent(Log_External, m_sipDialogueManager.DialogueTransfer, m_sipTransport, m_outboundProxy, replacesDialogue, oppositeDialogue, referTo.ToString(), customer.CustomerUsername, customer.AdminId); DialPlanScriptContext scriptContext = new DialPlanScriptContext( Log_External, m_sipTransport, CreateDialogueBridge, m_outboundProxy, uas, dialPlan, GetSIPProviders_External(p => p.Owner == username, null, 0, Int32.MaxValue), m_traceDirectory, null, customer, null, GetCanonicalDomain_External); //scriptContext.DialPlanComplete += () => { DecrementCustomerExecutionCount(customer); }; m_dialPlanEngine.Execute(scriptContext, uas, SIPCallDirection.Out, CreateDialogueBridge, this); return uas; } } } } catch (ApplicationException) { throw; } catch (Exception excp) { logger.Error("Exception SIPCallManager BlindTransfer. " + excp.Message); //if (wasExecutionCountIncremented) //{ //DecrementDialPlanExecutionCount(dialPlan, customer.Id, originalExecutionCount); //DecrementCustomerExecutionCount(customer); //} throw; } }
/// <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); } }
/// <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); } }