Inheritance: DialPlanContext
        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);
            }
        }
Beispiel #6
0
        /// <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);
            }
        }