See "Chapter 12 Dialogs" in RFC3261.
The standard states that there are two independent CSeq's for a dialogue: one for requests from the UAC and for requests from the UAS. In practice it's been noted that is a UAS (initial UAS) sends an in-dialogue request with a CSeq less than the UAC's CSeq it can cause problems. To avoid this issue when generating requests the remote CSeq is always used.
        public SIPEventDialog(string id, string state, SIPDialogue sipDialogue)
        {
            ID = id;
            State = state;

            if (sipDialogue != null)
            {
                CallID = sipDialogue.CallId;
                LocalTag = sipDialogue.LocalTag;
                RemoteTag = sipDialogue.RemoteTag;
                Direction = (sipDialogue.Direction == SIPCallDirection.In) ? SIPEventDialogDirectionEnum.recipient : SIPEventDialogDirectionEnum.initiator;
                Duration = Convert.ToInt32((DateTimeOffset.UtcNow - sipDialogue.Inserted).TotalSeconds % Int32.MaxValue);
                //LocalParticipant = new SIPEventDialogParticipant(sipDialogue.LocalUserField.Name, sipDialogue.LocalUserField.URI, null, sipDialogue.CSeq, sipDialogue.SDP);
                LocalParticipant = new SIPEventDialogParticipant(sipDialogue.LocalUserField.Name, sipDialogue.LocalUserField.URI, null, sipDialogue.CSeq);
                //RemoteParticipant = new SIPEventDialogParticipant(sipDialogue.RemoteUserField.Name, sipDialogue.RemoteUserField.URI, sipDialogue.RemoteTarget, sipDialogue.CSeq, sipDialogue.RemoteSDP);
                RemoteParticipant = new SIPEventDialogParticipant(sipDialogue.RemoteUserField.Name, sipDialogue.RemoteUserField.URI, sipDialogue.RemoteTarget, sipDialogue.CSeq);
                BridgeID = (sipDialogue.BridgeId != Guid.Empty) ? sipDialogue.BridgeId.ToString() : null;
                SwitchboardOwner = (sipDialogue.SwitchboardOwner != null) ? sipDialogue.SwitchboardOwner : null;

                if (sipDialogue.Direction == SIPCallDirection.In)
                {
                    RemoteParticipant.CRMPersonName = sipDialogue.CRMPersonName;
                    RemoteParticipant.CRMCompanyName = sipDialogue.CRMCompanyName;
                    RemoteParticipant.CRMPictureURL = sipDialogue.CRMPictureURL;
                    LocalParticipant.SwitchboardLineName = sipDialogue.SwitchboardLineName;
                }
                else if (sipDialogue.Direction == SIPCallDirection.Out)
                {
                    LocalParticipant.CRMPersonName = sipDialogue.CRMPersonName;
                    LocalParticipant.CRMCompanyName = sipDialogue.CRMCompanyName;
                    LocalParticipant.CRMPictureURL = sipDialogue.CRMPictureURL;
                    RemoteParticipant.SwitchboardLineName = sipDialogue.SwitchboardLineName;
                }
            }
        }
        /// <summary>
        /// Attempts to reinvite an existing end of a call by sending a new SDP.
        /// </summary>
        /// <param name="dialogue">The dialogue describing the end of the call to be re-invited.</param>
        /// <param name="newSDP">The session description for the new dialogue desired.</param>
        public void ReInvite(SIPDialogue dialogue, SIPDialogue substituteDialogue)
        {
            try
            {
                string replacementSDP = substituteDialogue.RemoteSDP;
                // Determine whether the SDP needs to be mangled.
                IPEndPoint dialogueSDPSocket = SDP.GetSDPRTPEndPoint(dialogue.RemoteSDP);
                IPEndPoint replacementSDPSocket = SDP.GetSDPRTPEndPoint(substituteDialogue.RemoteSDP);
                bool wasMangled = false;

                if (!IPSocket.IsPrivateAddress(dialogueSDPSocket.Address.ToString()) && IPSocket.IsPrivateAddress(replacementSDPSocket.Address.ToString()))
                {
                    // The SDP being used in the re-invite uses a private IP address but the SDP on the ua it's being sent to does not so mangle.
                    string publicIPAddress = (PublicIPAddress != null) ? PublicIPAddress.ToString() : IPSocket.ParseHostFromSocket(substituteDialogue.RemoteTarget.Host);
                    replacementSDP = SIPPacketMangler.MangleSDP(replacementSDP, publicIPAddress, out wasMangled);
                }

                if (wasMangled)
                {
                    logger.Debug("The SDP being used in a re-INVITE was mangled to " + SDP.GetSDPRTPEndPoint(replacementSDP) + ".");
                }

                // Check whether there is a need to send the re-invite by comparing the new SDP being sent with what has already been sent.
                if (dialogue.SDP == replacementSDP)
                {
                    logger.Debug("A reinvite was not sent to " + dialogue.RemoteTarget.ToString() + " as the SDP has not changed.");
                }
                else
                {
                    logger.Debug("Reinvite SDP being sent to " + dialogue.RemoteTarget.ToString() + ":\r\n" + replacementSDP);

                    dialogue.CSeq = dialogue.CSeq + 1;
                    m_sipDialoguePersistor.UpdateProperty(dialogue.Id, "CSeq", dialogue.CSeq);
                    SIPEndPoint localSIPEndPoint = (m_outboundProxy != null) ? m_sipTransport.GetDefaultTransportContact(m_outboundProxy.Protocol) : m_sipTransport.GetDefaultTransportContact(SIPProtocolsEnum.udp);
                    SIPRequest reInviteReq = GetInviteRequest(dialogue, localSIPEndPoint, replacementSDP);

                    SIPEndPoint reinviteEndPoint = null;

                    // If the outbound proxy is a loopback address, as it will normally be for local deployments, then it cannot be overriden.
                    if (m_outboundProxy != null && IPAddress.IsLoopback(m_outboundProxy.Address))
                    {
                        reInviteReq.Header.ProxySendFrom = dialogue.ProxySendFrom;
                        reinviteEndPoint = m_outboundProxy;
                    }
                    if (!dialogue.ProxySendFrom.IsNullOrBlank())
                    {
                        reInviteReq.Header.ProxySendFrom = dialogue.ProxySendFrom;
                        // The proxy will always be listening on UDP port 5060 for requests from internal servers.
                        reinviteEndPoint = new SIPEndPoint(SIPProtocolsEnum.udp, new IPEndPoint(SIPEndPoint.ParseSIPEndPoint(dialogue.ProxySendFrom).Address, m_defaultSIPPort));
                    }
                    else
                    {
                        SIPDNSLookupResult lookupResult = m_sipTransport.GetRequestEndPoint(reInviteReq, m_outboundProxy, false);
                        if (lookupResult.LookupError != null)
                        {
                            logger.Warn("ReInvite Failed to resolve " + lookupResult.URI.Host + ".");
                        }
                        else
                        {
                            reinviteEndPoint = lookupResult.GetSIPEndPoint();
                        }
                    }

                    if (reinviteEndPoint != null)
                    {
                        UACInviteTransaction reInviteTransaction = m_sipTransport.CreateUACTransaction(reInviteReq, reinviteEndPoint, localSIPEndPoint, reinviteEndPoint);
                        reInviteTransaction.CDR = null; // Don't want CDRs on re-invites.
                        reInviteTransaction.UACInviteTransactionFinalResponseReceived += ReInviteTransactionFinalResponseReceived;
                        reInviteTransaction.SendInviteRequest(reinviteEndPoint, reInviteReq);
                    }
                    else
                    {
                        throw new ApplicationException("Could not forward re-invite as request end point could not be determined.\r\n" + reInviteReq.ToString());
                    }
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception CallManager ReInvite. " + excp.Message);
                throw excp;
            }
        }
        private void ServerFinalResponseReceived(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPTransaction sipTransaction, SIPResponse sipResponse)
        {
            try
            {
                //if (Thread.CurrentThread.Name.IsNullOrBlank())
                //{
                //    Thread.CurrentThread.Name = THREAD_NAME + DateTime.Now.ToString("HHmmss") + "-" + Crypto.GetRandomString(3);
                //}

                Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.UserAgentClient, SIPMonitorEventTypesEnum.DialPlan, "Response " + sipResponse.StatusCode + " " + sipResponse.ReasonPhrase + " for " + m_serverTransaction.TransactionRequest.URI.ToString() + ".", Owner));
                //m_sipTrace += "Received " + DateTime.Now.ToString("dd MMM yyyy HH:mm:ss") + " " + localEndPoint + "<-" + remoteEndPoint + "\r\n" + sipResponse.ToString();

                m_serverTransaction.UACInviteTransactionInformationResponseReceived -= ServerInformationResponseReceived;
                m_serverTransaction.UACInviteTransactionFinalResponseReceived -= ServerFinalResponseReceived;
                m_serverTransaction.TransactionTraceMessage -= TransactionTraceMessage;

                if (m_callCancelled && sipResponse.Status == SIPResponseStatusCodesEnum.RequestTerminated)
                {
                    // No action required. Correctly received request terminated on an INVITE we cancelled.
                }
                else if (m_callCancelled)
                {
                    #region Call has been cancelled, hangup.

                    if (m_hungupOnCancel)
                    {
                        Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.UserAgentClient, SIPMonitorEventTypesEnum.DialPlan, "A cancelled call to " + m_sipCallDescriptor.Uri + " has been answered AND has already been hungup, no further action being taken.", Owner));
                    }
                    else
                    {
                        m_hungupOnCancel = true;

                        Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.UserAgentClient, SIPMonitorEventTypesEnum.DialPlan, "A cancelled call to " + m_sipCallDescriptor.Uri + " has been answered, hanging up.", Owner));

                        if (sipResponse.Header.Contact != null && sipResponse.Header.Contact.Count > 0)
                        {
                            SIPURI byeURI = sipResponse.Header.Contact[0].ContactURI;
                            SIPRequest byeRequest = GetByeRequest(sipResponse, byeURI, localSIPEndPoint);

                            //SIPEndPoint byeEndPoint = m_sipTransport.GetRequestEndPoint(byeRequest, m_outboundProxy, true);

                            // if (byeEndPoint != null)
                            // {
                            SIPNonInviteTransaction byeTransaction = m_sipTransport.CreateNonInviteTransaction(byeRequest, null, localSIPEndPoint, m_outboundProxy);
                            byeTransaction.SendReliableRequest();
                            // }
                            // else
                            // {
                            //     Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.UserAgentClient, SIPMonitorEventTypesEnum.DialPlan, "Could not end BYE on cancelled call as request end point could not be determined " + byeRequest.URI.ToString(), Owner));
                            //}
                        }
                        else
                        {
                            Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.UserAgentClient, SIPMonitorEventTypesEnum.DialPlan, "No contact header provided on response for cancelled call to " + m_sipCallDescriptor.Uri + " no further action.", Owner));
                        }
                    }

                    #endregion
                }
                else if (sipResponse.Status == SIPResponseStatusCodesEnum.ProxyAuthenticationRequired || sipResponse.Status == SIPResponseStatusCodesEnum.Unauthorised)
                {
                    //logger.Debug("AuthReqd Final response " + sipResponse.StatusCode + " " + sipResponse.ReasonPhrase + " for " + m_serverTransaction.TransactionRequest.URI.ToString() + ".");

                    #region Authenticate client call to third party server.

                    if (!m_callCancelled)
                    {
                        if (m_sipCallDescriptor.Password.IsNullOrBlank())
                        {
                            // No point trying to authenticate if there is no password to use.
                            Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.UserAgentClient, SIPMonitorEventTypesEnum.DialPlan, "Forward leg failed, authentication was requested but no credentials were available.", Owner));
                            FireCallFailed(this, "Authentication requested when no credentials available");
                        }
                        else if (m_serverAuthAttempts == 0)
                        {
                            m_serverAuthAttempts = 1;

                            // Resend INVITE with credentials.
                            string username = (m_sipCallDescriptor.AuthUsername != null && m_sipCallDescriptor.AuthUsername.Trim().Length > 0) ? m_sipCallDescriptor.AuthUsername : m_sipCallDescriptor.Username;
                            SIPAuthorisationDigest authRequest = sipResponse.Header.AuthenticationHeader.SIPDigest;
                            authRequest.SetCredentials(username, m_sipCallDescriptor.Password, m_sipCallDescriptor.Uri, SIPMethodsEnum.INVITE.ToString());

                            SIPRequest authInviteRequest = m_serverTransaction.TransactionRequest;

                            if (SIPProviderMagicJack.IsMagicJackRequest(sipResponse))
                            {
                                authInviteRequest.Header.AuthenticationHeader = SIPProviderMagicJack.GetAuthenticationHeader(sipResponse);
                            }
                            else
                            {
                                authInviteRequest.Header.AuthenticationHeader = new SIPAuthenticationHeader(authRequest);
                                authInviteRequest.Header.AuthenticationHeader.SIPDigest.Response = authRequest.Digest;
                            }

                            authInviteRequest.Header.Vias.TopViaHeader.Branch = CallProperties.CreateBranchId();
                            authInviteRequest.Header.CSeq = authInviteRequest.Header.CSeq + 1;

                            // Create a new UAC transaction to establish the authenticated server call.
                            var originalCallTransaction = m_serverTransaction;
                            m_serverTransaction = m_sipTransport.CreateUACTransaction(authInviteRequest, m_serverEndPoint, localSIPEndPoint, m_outboundProxy);
                            if (m_serverTransaction.CDR != null)
                            {
                                m_serverTransaction.CDR.Owner = Owner;
                                m_serverTransaction.CDR.AdminMemberId = AdminMemberId;
                                m_serverTransaction.CDR.AccountCode = AccountCode;
                                m_serverTransaction.CDR.Rate = Rate;

                                // Transfer any credit reservations from the original call to the new call.
                                m_serverTransaction.CDR.SecondsReserved = originalCallTransaction.CDR.SecondsReserved;
                                m_serverTransaction.CDR.Cost = originalCallTransaction.CDR.Cost;
                                originalCallTransaction.CDR.SecondsReserved = 0;
                                originalCallTransaction.CDR.Cost = 0;
                                originalCallTransaction.CDR.ReconciliationResult = "reallocated";
                                originalCallTransaction.CDR.IsHangingUp = true;

                                logger.Debug("RTCC reservation was reallocated from CDR " + originalCallTransaction.CDR.CDRId + " to " + m_serverTransaction.CDR.CDRId + " for owner " + Owner + ".");
                            }
                            m_serverTransaction.UACInviteTransactionInformationResponseReceived += ServerInformationResponseReceived;
                            m_serverTransaction.UACInviteTransactionFinalResponseReceived += ServerFinalResponseReceived;
                            m_serverTransaction.UACInviteTransactionTimedOut += ServerTimedOut;
                            m_serverTransaction.TransactionTraceMessage += TransactionTraceMessage;

                            //logger.Debug("Sending authenticated switchcall INVITE to " + ForwardedCallStruct.Host + ".");
                            m_serverTransaction.SendInviteRequest(m_serverEndPoint, authInviteRequest);
                            //m_sipTrace += "Sending " + DateTime.Now.ToString("dd MMM yyyy HH:mm:ss") + " " + localEndPoint + "->" + ForwardedTransaction.TransactionRequest.GetRequestEndPoint() + "\r\n" + ForwardedTransaction.TransactionRequest.ToString();
                        }
                        else
                        {
                            //logger.Debug("Authentication of client call to switch server failed.");
                            FireCallFailed(this, "Authentication with provided credentials failed");
                        }
                    }

                    #endregion
                }
                else
                {
                    if (sipResponse.StatusCode >= 200 && sipResponse.StatusCode <= 299)
                    {

                        if (sipResponse.Body.IsNullOrBlank())
                        {
                            Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Body on UAC response was empty.", Owner));
                        }
                        else if (m_sipCallDescriptor.ContentType == m_sdpContentType)
                        {
                            if (!m_sipCallDescriptor.MangleResponseSDP)
                            {
                                IPEndPoint sdpEndPoint = SDP.GetSDPRTPEndPoint(sipResponse.Body);
                                string sdpSocket = (sdpEndPoint != null) ? sdpEndPoint.ToString() : "could not determine";
                                Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "SDP on UAC response was set to NOT mangle, RTP socket " + sdpEndPoint.ToString() + ".", Owner));
                            }
                            else
                            {
                                //m_callInProgress = false; // the call is now established
                                //logger.Debug("Final response " + sipResponse.StatusCode + " " + sipResponse.ReasonPhrase + " for " + ForwardedTransaction.TransactionRequest.URI.ToString() + ".");
                                // Determine of response SDP should be mangled.

                                IPEndPoint sdpEndPoint = SDP.GetSDPRTPEndPoint(sipResponse.Body);
                                //Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "UAC response SDP was mangled from sdp=" + sdpEndPoint.Address.ToString() + ", proxyfrom=" + sipResponse.Header.ProxyReceivedFrom + ", mangle=" + m_sipCallDescriptor.MangleResponseSDP + ".", null));
                                if (sdpEndPoint != null)
                                {
                                    if (!IPSocket.IsPrivateAddress(sdpEndPoint.Address.ToString()))
                                    {
                                        Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "SDP on UAC response had public IP not mangled, RTP socket " + sdpEndPoint.ToString() + ".", Owner));
                                    }
                                    else
                                    {
                                        bool wasSDPMangled = false;
                                        string publicIPAddress = null;

                                        if (!sipResponse.Header.ProxyReceivedFrom.IsNullOrBlank())
                                        {
                                            IPAddress remoteUASAddress = SIPEndPoint.ParseSIPEndPoint(sipResponse.Header.ProxyReceivedFrom).Address;
                                            if (IPSocket.IsPrivateAddress(remoteUASAddress.ToString()) && m_sipCallDescriptor.MangleIPAddress != null)
                                            {
                                                // If the response has arrived here on a private IP address then it must be
                                                // for an local version install and an incoming call that needs it's response mangled.
                                                publicIPAddress = m_sipCallDescriptor.MangleIPAddress.ToString();
                                            }
                                            else
                                            {
                                                publicIPAddress = remoteUASAddress.ToString();
                                            }
                                        }
                                        else if (!IPSocket.IsPrivateAddress(remoteEndPoint.Address.ToString()))
                                        {
                                            publicIPAddress = remoteEndPoint.Address.ToString();
                                        }
                                        else if (m_sipCallDescriptor.MangleIPAddress != null)
                                        {
                                            publicIPAddress = m_sipCallDescriptor.MangleIPAddress.ToString();
                                        }

                                        if (publicIPAddress != null)
                                        {
                                            sipResponse.Body = SIPPacketMangler.MangleSDP(sipResponse.Body, publicIPAddress, out wasSDPMangled);
                                        }

                                        if (wasSDPMangled)
                                        {
                                            Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "SDP on UAC response had RTP socket mangled from " + sdpEndPoint.ToString() + " to " + publicIPAddress + ":" + sdpEndPoint.Port + ".", Owner));
                                        }
                                        else if (sdpEndPoint != null)
                                        {
                                            Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "SDP on UAC response could not be mangled, RTP socket " + sdpEndPoint.ToString() + ".", Owner));
                                        }
                                    }
                                }
                                else
                                {
                                    Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "SDP RTP socket on UAC response could not be determined.", Owner));
                                }
                            }
                        }

                        m_sipDialogue = new SIPDialogue(m_serverTransaction, Owner, AdminMemberId);
                        m_sipDialogue.CallDurationLimit = m_sipCallDescriptor.CallDurationLimit;

                        // Set switchboard dialogue values from the answered response or from dialplan set values.
                        //m_sipDialogue.SwitchboardCallerDescription = sipResponse.Header.SwitchboardCallerDescription;
                        m_sipDialogue.SwitchboardLineName = sipResponse.Header.SwitchboardLineName;
                        m_sipDialogue.CRMPersonName = sipResponse.Header.CRMPersonName;
                        m_sipDialogue.CRMCompanyName = sipResponse.Header.CRMCompanyName;
                        m_sipDialogue.CRMPictureURL = sipResponse.Header.CRMPictureURL;

                        if (m_sipCallDescriptor.SwitchboardHeaders != null)
                        {
                            //if (!m_sipCallDescriptor.SwitchboardHeaders.SwitchboardDialogueDescription.IsNullOrBlank())
                            //{
                            //    m_sipDialogue.SwitchboardDescription = m_sipCallDescriptor.SwitchboardHeaders.SwitchboardDialogueDescription;
                            //}

                            m_sipDialogue.SwitchboardLineName = m_sipCallDescriptor.SwitchboardHeaders.SwitchboardLineName;
                            m_sipDialogue.SwitchboardOwner = m_sipCallDescriptor.SwitchboardHeaders.SwitchboardOwner;
                        }
                    }

                    FireCallAnswered(this, sipResponse);
                }
            }
            catch (Exception excp)
            {
                Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.UserAgentClient, SIPMonitorEventTypesEnum.Error, "Exception ServerFinalResponseReceived. " + excp.Message, Owner));
            }
        }
        public SIPDialogue Answer(string contentType, string body, string toTag, SIPDialogue answeredDialogue, SIPDialogueTransferModesEnum transferMode)
        {
            try
            {
                if (m_uasTransaction.TransactionFinalResponse != null)
                {
                    Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "UAS Answer was called on an already answered call, ignoring.", m_owner));
                    return null;
                }
                else
                {
                    if (UASStateChanged != null)
                    {
                        UASStateChanged(this, SIPResponseStatusCodesEnum.Ok, null);
                    }

                    if (!toTag.IsNullOrBlank())
                    {
                        m_uasTransaction.SetLocalTag(toTag);
                    }

                    SIPResponse okResponse = m_uasTransaction.GetOkResponse(m_uasTransaction.TransactionRequest, m_uasTransaction.TransactionRequest.LocalSIPEndPoint, contentType, body);

                    //added for gruu compatibility
                    if (!m_gruu.IsNullOrBlank())
                    {
                        okResponse.Header.Contact[0].ContactURI.Parameters.Set(SIPCallDescriptor.GRUU_KEY,m_gruu);
                    }

                    if (body != null)
                    {
                        okResponse.Header.ContentType = contentType;
                        okResponse.Header.ContentLength = body.Length;
                        okResponse.Body = body;
                    }

                    m_uasTransaction.SendFinalResponse(okResponse);

                    m_sipDialogue = new SIPDialogue(m_uasTransaction, m_owner, m_adminMemberId);
                    m_sipDialogue.TransferMode = transferMode;

                    return m_sipDialogue;
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception SIPServerUserAgent Answer. " + excp.Message);
                throw;
            }
        }
        public void Call(SIPCallDescriptor descriptor)
        {
            try
            {
                CallDescriptor = descriptor;
                SIPURI destinationURI = SIPURI.ParseSIPURIRelaxed(descriptor.Uri);

                bool wasSDPMangled = false;
                IPEndPoint sdpEndPoint = null;
                if (descriptor.MangleIPAddress != null)
                {
                    sdpEndPoint = SDP.GetSDPRTPEndPoint(descriptor.Content);
                    if (sdpEndPoint != null)
                    {
                        descriptor.Content = SIPPacketMangler.MangleSDP(descriptor.Content, descriptor.MangleIPAddress.ToString(), out wasSDPMangled);
                    }
                }

                if (wasSDPMangled)
                {
                    Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "SDP on Google Voice call had RTP socket mangled from " + sdpEndPoint.ToString() + " to " + descriptor.MangleIPAddress.ToString() + ":" + sdpEndPoint.Port + ".", Owner));
                }
                else if (sdpEndPoint != null)
                {
                    Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "SDP on Google Voice call could not be mangled, using original RTP socket of " + sdpEndPoint.ToString() + ".", Owner));
                }

                SIPDialogue = m_googleVoiceCall.InitiateCall(descriptor.Username, descriptor.Password, descriptor.CallbackNumber, destinationURI.User, descriptor.CallbackPattern, descriptor.CallbackPhoneType, MAX_CALLBACK_WAIT_TIME, descriptor.ContentType, descriptor.Content);

                if (SIPDialogue != null)
                {
                    CallAnswered(this, null);
                }
                else
                {
                    CallFailed(this, "Google Voice call failed.");
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception GoogleVoiceCallAgent Call. " + excp.Message);
                CallFailed(this, excp.Message);
            }
        }
示例#6
0
        private void OnCallHungup(SIPDialogue sipDialogue)
        {
            try
            {
                var transferEntry = m_inProgressTransfers.Where(x => x.Key.CallId == sipDialogue.CallId).FirstOrDefault();

                if(transferEntry.Key != null)
                {
                    logger.Debug("A matching in progress transfer was found for a hungup dialogue.");
                    transferEntry.Value.PendingLegHungup();
                    m_inProgressTransfers.Remove(transferEntry.Key);
                }
            }
            catch(Exception excp)
            {
                logger.Error("Exception SIPCallManager.OnCallHungup. " + excp);
            }
        }
示例#7
0
 public void CreateDialogueBridge(SIPDialogue clientDiaglogue, SIPDialogue forwardedDialogue, string owner)
 {
     m_sipDialogueManager.CreateDialogueBridge(clientDiaglogue, forwardedDialogue, owner);
 }
        /// <summary>
        /// This method takes the necessary actions to terminate a bridged call.
        /// </summary>
        /// <param name="sipDialogue">The dialogue that the BYE request was received on.</param>
        /// <param name="hangupCause">If present an informational field to indicate the hangup cause.</param>
        /// <param name="sendBYEForOriginDialogue">If true means a BYE should be sent for the origin dialogue as well. This is used when a 3rd party
        /// call control agent is attempting to hangup a call.</param>
        public void CallHungup(SIPDialogue sipDialogue, string hangupCause, bool sendBYEForOriginDialogue)
        {
            try
            {
                if (sipDialogue != null)
                {
                    //logger.Debug("BYE received on dialogue " + sipDialogue.DialogueName + ".");
                    HangupDialogue(sipDialogue, hangupCause, sendBYEForOriginDialogue);

                    if (sipDialogue.BridgeId != Guid.Empty)
                    {
                        SIPDialogue orphanedDialogue = GetOppositeDialogue(sipDialogue);
                        if (orphanedDialogue != null)
                        {
                            HangupDialogue(orphanedDialogue, m_remoteHangupCause, true);
                        }
                    }
                    else
                    {
                        logger.Warn("No bridge could be found for hungup call.");
                    }
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception CallManager CallHungup. " + excp.Message);
            }
        }
        private void HangupDialogue(SIPDialogue dialogue, string hangupCause, bool sendBye)
        {
            try
            {
                //logger.Debug("Hanging up orphaned dialogue " + dialogue.DialogueName + ".");

                if (dialogue.CDRId != Guid.Empty)
                {
                    SIPCDRAsset cdr = m_sipCDRPersistor.Get(dialogue.CDRId);
                    if (cdr != null)
                    {
                        cdr.BridgeId = dialogue.BridgeId.ToString();
                        cdr.Hungup(hangupCause);
                    }
                    else
                    {
                        logger.Warn("CDR could not be found for remote dialogue in SIPCallManager CallHungup.");
                    }
                }
                else
                {
                    logger.Warn("There was no CDR attached to orphaned dialogue in SIPCallManager CallHungup.");
                }

                if (sendBye)
                {
                    dialogue.Hangup(m_sipTransport, m_outboundProxy);
                }

                m_sipDialoguePersistor.Delete(new SIPDialogueAsset(dialogue));

                SIPEndPoint orphanedDialogueRemoteEP = (IPSocket.IsIPSocket(dialogue.RemoteTarget.Host)) ? SIPEndPoint.ParseSIPEndPoint(dialogue.RemoteTarget.Host) : null;
                Log_External(new SIPMonitorMachineEvent(SIPMonitorMachineEventTypesEnum.SIPDialogueRemoved, dialogue.Owner, dialogue.Id.ToString(), dialogue.LocalUserField.URI));
            }
            catch (Exception excp)
            {
                logger.Error("Exception HangupDialogue. " + excp.Message);
            }
        }
        /// <summary>
        /// Performs an attended transfer based on a REFER request with a Replaces parameter on the Refer-To header.
        /// </summary>
        /// <param name="dialogue">The dialogue matching the the REFER request headers (Call-ID, To tag and From tag).</param>
        /// <param name="referTransaction">The REFER request.</param>
        /// <param name="localEndPoint">The local SIP end point the REFER request was received on.</param>
        /// <param name="remoteEndPoint">The remote SIP end point the REFER request was received from.</param>
        private void ProcessAttendedRefer(SIPDialogue dialogue, SIPNonInviteTransaction referTransaction, SIPRequest referRequest, SIPEndPoint localEndPoint, SIPEndPoint remoteEndPoint)
        {
            try
            {
                Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Initiating attended transfer.", dialogue.Owner));
                SIPUserField referToField = SIPUserField.ParseSIPUserField(referRequest.Header.ReferTo);

                if (referToField == null)
                {
                    Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Error on transfer, could not parse Refer-To header: " + referRequest.Header.ReferTo + ".", dialogue.Owner));
                    SIPResponse errorResponse = SIPTransport.GetResponse(referRequest, SIPResponseStatusCodesEnum.BadRequest, "Could not parse Refer-To header");
                    referTransaction.SendFinalResponse(errorResponse);
                }
                else
                {
                    string replaces = referToField.URI.Headers.Get(m_referReplacesParameter);

                    SIPDialogue replacesDialogue = GetDialogue(replaces);
                    if (replacesDialogue == null)
                    {
                        Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Could not locate the dialogue for the Replaces parameter on an attended transfer.", dialogue.Owner));
                        SIPResponse errorResponse = SIPTransport.GetResponse(referRequest, SIPResponseStatusCodesEnum.BadRequest, "Could not locate replaced dialogue");
                        referTransaction.SendFinalResponse(errorResponse);
                    }
                    else
                    {
                        logger.Debug("REFER dialogue being replaced " + replacesDialogue.DialogueName + ".");

                        Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Replacement dialogue found on Refer, accepting.", dialogue.Owner));

                        bool sendNotifications = true;
                        if(!referRequest.Header.ReferSub.IsNullOrBlank())
                        {
                            Boolean.TryParse(referRequest.Header.ReferSub, out sendNotifications);
                        }

                        SIPDialogue remainingDialogue = GetOppositeDialogue(replacesDialogue);
                        SIPDialogue remaining2Dialogue = GetOppositeDialogue(dialogue);

                        logger.Debug("REFER dialogue remaining " + remainingDialogue.DialogueName + ".");

                        Guid newBridgeId = Guid.NewGuid();
                        remainingDialogue.BridgeId = newBridgeId;
                        remainingDialogue.CSeq++;
                        remaining2Dialogue.BridgeId = newBridgeId;
                        remaining2Dialogue.CSeq++;

                        m_sipDialoguePersistor.Update(new SIPDialogueAsset(remainingDialogue));
                        m_sipDialoguePersistor.Update(new SIPDialogueAsset(remaining2Dialogue));

                        Log_External(new SIPMonitorMachineEvent(SIPMonitorMachineEventTypesEnum.SIPDialogueUpdated, remainingDialogue.Owner, remainingDialogue.Id.ToString(), remainingDialogue.LocalUserField.URI));
                        Log_External(new SIPMonitorMachineEvent(SIPMonitorMachineEventTypesEnum.SIPDialogueUpdated, remaining2Dialogue.Owner, remaining2Dialogue.Id.ToString(), remaining2Dialogue.LocalUserField.URI));
                        Log_External(new SIPMonitorMachineEvent(SIPMonitorMachineEventTypesEnum.SIPDialogueTransfer, remainingDialogue.Owner, remainingDialogue.Id.ToString(), remainingDialogue.LocalUserField.URI));

                        SIPResponse acceptedResponse = SIPTransport.GetResponse(referRequest, SIPResponseStatusCodesEnum.Accepted, null);
                        referTransaction.SendFinalResponse(acceptedResponse);

                        if (sendNotifications)
                        {
                            SendNotifyRequestForRefer(referRequest, dialogue, localEndPoint, SIPResponseStatusCodesEnum.Trying, null);
                        }

                        logger.Debug("Reinviting " + remainingDialogue.DialogueName + " with " + remaining2Dialogue.DialogueName + ".");

                        ReInvite(remainingDialogue, remaining2Dialogue);
                        ReInvite(remaining2Dialogue, remainingDialogue);

                        Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Transfer dialogue re-invites complete.", dialogue.Owner));

                        if (sendNotifications)
                        {
                            SendNotifyRequestForRefer(referRequest, dialogue, localEndPoint, SIPResponseStatusCodesEnum.Ok, null);
                        }

                        // Hangup redundant dialogues.
                        logger.Debug("Hanging up redundant dialogues post transfer.");
                        logger.Debug("Hanging up " + dialogue.DialogueName + ".");
                        dialogue.Hangup(m_sipTransport, m_outboundProxy);
                        CallHungup(dialogue, "Attended transfer", false);
                        logger.Debug("Hanging up " + replacesDialogue.DialogueName + ".");
                        replacesDialogue.Hangup(m_sipTransport, m_outboundProxy);
                        CallHungup(replacesDialogue, "Attended transfer", false);
                    }
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception ProcessAttendedRefer. " + excp.Message);
                throw;
            }
        }
        /// <summary>
        /// Constructs a NOTIFY request to send within the implicit subscription created when processing a REFER request.
        /// </summary>
        /// <remarks>
        /// From RFC 3515 2.4.5:
        /// The body of a NOTIFY MUST begin with a SIP Response Status-Line...
        /// </remarks>
        /// <param name="referRequest">The REFER request that created the implicit refer event subscription.</param>
        /// <param name="referDialogue">The dialogue that the REFER request has been received within.</param>
        /// <param name="referResponse">The response that has been received to whatever is doing the post-REFER processing.</param>
        /// <param name="localEndPoint">The local SIP end point that the NOTIFY request will be sent from.</param>
        /// <returns>A NOTIFY request suitable for sending to the remote end of the REFER initiating dialogue.</returns>
        private SIPRequest GetNotifyRequest(SIPRequest referRequest, SIPDialogue referDialogue, SIPResponse referResponse, SIPEndPoint localEndPoint)
        {
            try
            {
                SIPRequest notifyRequest = new SIPRequest(SIPMethodsEnum.NOTIFY, referRequest.Header.Contact[0].ContactURI);
                notifyRequest.Header = new SIPHeader(SIPFromHeader.ParseFromHeader(referDialogue.LocalUserField.ToString()), SIPToHeader.ParseToHeader(referDialogue.RemoteUserField.ToString()), referDialogue.CSeq, referDialogue.CallId);
                notifyRequest.Header.Event = m_referNotifyEventValue; // + ";id=" + referRequest.Header.CSeq;
                notifyRequest.Header.CSeqMethod = SIPMethodsEnum.NOTIFY;
                notifyRequest.Header.SubscriptionState = (referResponse.StatusCode >= 200) ? "terminated;reason=noresource" : "active;expires=60";
                notifyRequest.Header.ContentType = m_referNotifyContentType;

                SIPViaHeader viaHeader = new SIPViaHeader(localEndPoint, CallProperties.CreateBranchId());
                notifyRequest.Header.Vias.PushViaHeader(viaHeader);

                notifyRequest.Body = (referResponse.SIPVersion + " " + referResponse.StatusCode + " " + referResponse.ReasonPhrase).Trim();
                notifyRequest.Header.ContentLength = notifyRequest.Body.Length;

                return notifyRequest;
            }
            catch (Exception excp)
            {
                logger.Error("Exception GetNotifyRequest. " + excp.Message);
                throw;
            }
        }
示例#12
0
        /// <summary>
        /// Establishes a new call with the client end tied to the proxy. Since the proxy will not be sending any audio the idea is that once
        /// the call is up it should be re-INVITED off somewhere else pronto to avoid the callee sitting their listening to dead air.
        /// </summary>
        /// <param name="dest1">The dial string of the first call to place.</param>
        /// <param name="dest2">The dial string of the second call to place.</param>
        /// <param name="delaySeconds">Delay in seconds before placing the first call. Gives the user a chance to hangup their phone if they are calling themselves back.</param>
        /// <param name="ringTimeoutLeg1">The ring timeout for the first call leg, If 0 the max timeout will be used.</param>
        /// <param name="ringTimeoutLeg1">The ring timeout for the second call leg, If 0 the max timeout will be used.</param>
        /// <param name="customHeadersCallLeg1">A | delimited string that contains a list of custom SIP headers to add to the INVITE request sent for the first call leg.</param>
        /// /// <param name="customHeadersCallLeg2">A | delimited string that contains a list of custom SIP headers to add to the INVITE request sent for the second call leg.</param>
        /// <returns>The result of the call.</returns>
        public void Callback(string dest1, string dest2, int delaySeconds, int ringTimeoutLeg1, int ringTimeoutLeg2, string customHeadersCallLeg1, string customHeadersCallLeg2)
        {
            try
            {
                if (delaySeconds > 0)
                {
                    delaySeconds = (delaySeconds > MAXCALLBACK_DELAY_SECONDS) ? MAXCALLBACK_DELAY_SECONDS : delaySeconds;
                    Log("Callback app delaying by " + delaySeconds + "s.");
                    Thread.Sleep(delaySeconds * 1000);
                }

                Log("Callback app commencing first leg to " + dest1 + ".");

                SIPEndPoint defaultUDPEP = m_sipTransport.GetDefaultSIPEndPoint(SIPProtocolsEnum.udp);

                SIPRequest firstLegDummyInviteRequest = GetCallbackInviteRequest(defaultUDPEP.GetIPEndPoint(), null);
                ringTimeoutLeg1 = (ringTimeoutLeg1 > 0) ? ringTimeoutLeg1 : MAXCALLBACK_RINGTIME_SECONDS;
                m_firstLegDialogue = Dial(dest1, ringTimeoutLeg1, 0, firstLegDummyInviteRequest, SIPCallDescriptor.ParseCustomHeaders(customHeadersCallLeg1));
                if (m_firstLegDialogue == null)
                {
                    Log("The first call leg to " + dest1 + " was unsuccessful.");
                    return;
                }

                SDP firstLegSDP = SDP.ParseSDPDescription(m_firstLegDialogue.RemoteSDP);
                string call1SDPIPAddress = firstLegSDP.Connection.ConnectionAddress;
                int call1SDPPort = firstLegSDP.Media[0].Port;
                Log("The first call leg to " + dest1 + " was successful, audio socket=" + call1SDPIPAddress + ":" + call1SDPPort + ".");

                Log("Callback app commencing second leg to " + dest2 + ".");

                SIPRequest secondLegDummyInviteRequest = GetCallbackInviteRequest(defaultUDPEP.GetIPEndPoint(), m_firstLegDialogue.RemoteSDP);
                ringTimeoutLeg2 = (ringTimeoutLeg2 > 0) ? ringTimeoutLeg2 : MAXCALLBACK_RINGTIME_SECONDS;
                SIPDialogue secondLegDialogue = Dial(dest2, ringTimeoutLeg2, 0, secondLegDummyInviteRequest, SIPCallDescriptor.ParseCustomHeaders(customHeadersCallLeg2));
                if (secondLegDialogue == null)
                {
                    Log("The second call leg to " + dest2 + " was unsuccessful.");
                    m_firstLegDialogue.Hangup(m_sipTransport, m_outboundProxy);
                    return;
                }

                SDP secondLegSDP = SDP.ParseSDPDescription(secondLegDialogue.RemoteSDP);
                string call2SDPIPAddress = secondLegSDP.Connection.ConnectionAddress;
                int call2SDPPort = secondLegSDP.Media[0].Port;
                Log("The second call leg to " + dest2 + " was successful, audio socket=" + call2SDPIPAddress + ":" + call2SDPPort + ".");

                m_callManager.CreateDialogueBridge(m_firstLegDialogue, secondLegDialogue, m_username);

                Log("Re-inviting Callback dialogues to each other.");

                m_callManager.ReInvite(m_firstLegDialogue, secondLegDialogue);
                //m_callManager.ReInvite(secondLegDialogue, m_firstLegDialogue.RemoteSDP);

                SendRTPPacket(call2SDPIPAddress + ":" + call2SDPPort, call1SDPIPAddress + ":" + call1SDPPort);
                SendRTPPacket(call1SDPIPAddress + ":" + call1SDPPort, call2SDPIPAddress + ":" + call2SDPPort);
            }
            catch (Exception excp)
            {
                logger.Error("Exception CallbackApp. " + excp);
                Log("Exception in Callback. " + excp);
            }
        }
示例#13
0
        /// <summary>
        /// Establishes a new call with the client end tied to the proxy. Since the proxy will not be sending any audio the idea is that once
        /// the call is up it should be re-INVITED off somewhere else pronto to avoid the callee sitting their listening to dead air.
        /// </summary>
        /// <param name="dest1">The dial string of the first call to place.</param>
        /// <param name="dest2">The dial string of the second call to place.</param>
        /// <param name="delaySeconds">Delay in seconds before placing the first call. Gives the user a chance to hangup their phone if they are calling themselves back.</param>
        /// <param name="ringTimeoutLeg1">The ring timeout for the first call leg, If 0 the max timeout will be used.</param>
        /// <param name="ringTimeoutLeg1">The ring timeout for the second call leg, If 0 the max timeout will be used.</param>
        /// <param name="customHeadersCallLeg1">A | delimited string that contains a list of custom SIP headers to add to the INVITE request sent for the first call leg.</param>
        /// /// <param name="customHeadersCallLeg2">A | delimited string that contains a list of custom SIP headers to add to the INVITE request sent for the second call leg.</param>
        /// <returns>The result of the call.</returns>
        public void Callback(string dest1, string dest2, int delaySeconds, int ringTimeoutLeg1, int ringTimeoutLeg2, string customHeadersCallLeg1, string customHeadersCallLeg2)
        {
            var ts = new CancellationTokenSource();
            CancellationToken ct = ts.Token;

            try
            {
                if (delaySeconds > 0)
                {
                    delaySeconds = (delaySeconds > MAXCALLBACK_DELAY_SECONDS) ? MAXCALLBACK_DELAY_SECONDS : delaySeconds;
                    Log("Callback app delaying by " + delaySeconds + "s.");
                    Thread.Sleep(delaySeconds * 1000);
                }

                Log("Callback app commencing first leg to " + dest1 + ".");

                SIPEndPoint defaultUDPEP = m_sipTransport.GetDefaultSIPEndPoint(SIPProtocolsEnum.udp);

                SIPRequest firstLegDummyInviteRequest = GetCallbackInviteRequest(defaultUDPEP.GetIPEndPoint(), null);
                ForkCall firstLegCall = new ForkCall(m_sipTransport, Log_External, m_callManager.QueueNewCall, null, m_username, m_adminMemberId, m_outboundProxy, m_callManager, null);
                m_firstLegDialogue = Dial(firstLegCall, dest1, ringTimeoutLeg1, 0, firstLegDummyInviteRequest, SIPCallDescriptor.ParseCustomHeaders(customHeadersCallLeg1));
                if (m_firstLegDialogue == null)
                {
                    Log("The first call leg to " + dest1 + " was unsuccessful.");
                    return;
                }

                // Persist the dialogue to the database so any hangup can be detected.
                m_sipDialoguePersistor.Add(new SIPDialogueAsset(m_firstLegDialogue));

                SDP firstLegSDP = SDP.ParseSDPDescription(m_firstLegDialogue.RemoteSDP);
                string call1SDPIPAddress = firstLegSDP.Connection.ConnectionAddress;
                int call1SDPPort = firstLegSDP.Media[0].Port;
                Log("The first call leg to " + dest1 + " was successful, audio socket=" + call1SDPIPAddress + ":" + call1SDPPort + ".");

                Log("Callback app commencing second leg to " + dest2 + ".");

                SIPRequest secondLegDummyInviteRequest = GetCallbackInviteRequest(defaultUDPEP.GetIPEndPoint(), m_firstLegDialogue.RemoteSDP);
                ForkCall secondLegCall = new ForkCall(m_sipTransport, Log_External, m_callManager.QueueNewCall, null, m_username, m_adminMemberId, m_outboundProxy, m_callManager, null);

                Task.Factory.StartNew(() =>
                {
                    while (true)
                    {
                        Thread.Sleep(CHECK_FIRST_LEG_FOR_HANGUP_PERIOD);

                        Console.WriteLine("Checking if first call leg is still up...");

                        if (ct.IsCancellationRequested)
                        {
                            Console.WriteLine("Checking first call leg task was cancelled.");
                            break;
                        }
                        else
                        {
                            // Check that the first call leg hasn't been hung up.
                            var dialog = m_sipDialoguePersistor.Get(m_firstLegDialogue.Id);
                            if (dialog == null)
                            {
                                Console.WriteLine("First call leg has been hungup.");

                                // The first call leg has been hungup while waiting for the second call.
                                Log("The first call leg was hungup while the second call leg was waiting for an answer.");
                                secondLegCall.CancelNotRequiredCallLegs(CallCancelCause.ClientCancelled);
                                break;
                            }
                        }
                    }

                    Console.WriteLine("Checking first call leg task finished...");
                }, ct);

                SIPDialogue secondLegDialogue = Dial(secondLegCall, dest2, ringTimeoutLeg2, 0, secondLegDummyInviteRequest, SIPCallDescriptor.ParseCustomHeaders(customHeadersCallLeg2));

                ts.Cancel();

                if (secondLegDialogue == null)
                {
                    Log("The second call leg to " + dest2 + " was unsuccessful.");
                    m_firstLegDialogue.Hangup(m_sipTransport, m_outboundProxy);
                    return;
                }

                // Check that the first call leg hasn't been hung up.
                var firstLegDialog = m_sipDialoguePersistor.Get(m_firstLegDialogue.Id);
                if (firstLegDialog == null)
                {
                    // The first call leg has been hungup while waiting for the second call.
                    Log("The first call leg was hungup while waiting for the second call leg.");
                    secondLegDialogue.Hangup(m_sipTransport, m_outboundProxy);
                    return;
                }

                SDP secondLegSDP = SDP.ParseSDPDescription(secondLegDialogue.RemoteSDP);
                string call2SDPIPAddress = secondLegSDP.Connection.ConnectionAddress;
                int call2SDPPort = secondLegSDP.Media[0].Port;
                Log("The second call leg to " + dest2 + " was successful, audio socket=" + call2SDPIPAddress + ":" + call2SDPPort + ".");

                // Persist the second leg dialogue and update the bridge ID on the first call leg.
                Guid bridgeId = Guid.NewGuid();
                secondLegDialogue.BridgeId = bridgeId;
                m_sipDialoguePersistor.Add(new SIPDialogueAsset(secondLegDialogue));
                m_sipDialoguePersistor.UpdateProperty(firstLegDialog.Id, "BridgeID", bridgeId.ToString());

                //m_callManager.CreateDialogueBridge(m_firstLegDialogue, secondLegDialogue, m_username);

                Log("Re-inviting Callback dialogues to each other.");

                m_callManager.ReInvite(m_firstLegDialogue, secondLegDialogue);
                //m_callManager.ReInvite(secondLegDialogue, m_firstLegDialogue.RemoteSDP);

                SendRTPPacket(call2SDPIPAddress + ":" + call2SDPPort, call1SDPIPAddress + ":" + call1SDPPort);
                SendRTPPacket(call1SDPIPAddress + ":" + call1SDPPort, call2SDPIPAddress + ":" + call2SDPPort);
            }
            catch (Exception excp)
            {
                logger.Error("Exception CallbackApp. " + excp);
                Log("Exception in Callback. " + excp);
            }
            finally
            {
                if (!ts.IsCancellationRequested)
                {
                    ts.Cancel();
                }
            }
        }
        private void ForwardInDialogueRequest(SIPDialogue dialogue, SIPTransaction inDialogueTransaction, SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint)
        {
            try
            {
                Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "In dialogue request " + inDialogueTransaction.TransactionRequest.Method + " received from for uri=" + inDialogueTransaction.TransactionRequest.URI.ToString() + ".", null));

                // Update the CSeq based on the latest received request.
                dialogue.CSeq = inDialogueTransaction.TransactionRequest.Header.CSeq;

                // Get the dialogue for the other end of the bridge.
                SIPDialogue bridgedDialogue = GetOppositeDialogue(dialogue);

                SIPEndPoint forwardSIPEndPoint = m_sipTransport.GetDefaultSIPEndPoint(new SIPEndPoint(bridgedDialogue.RemoteTarget));
                IPAddress remoteUAIPAddress = (inDialogueTransaction.TransactionRequest.Header.ProxyReceivedFrom.IsNullOrBlank()) ? remoteEndPoint.Address : SIPEndPoint.ParseSIPEndPoint(inDialogueTransaction.TransactionRequest.Header.ProxyReceivedFrom).Address;

                SIPRequest forwardedRequest = inDialogueTransaction.TransactionRequest.Copy();

                // Need to remove or reset headers from the copied request that conflict with the existing dialogue requests.
                forwardedRequest.Header.RecordRoutes = null;
                forwardedRequest.Header.MaxForwards = SIPConstants.DEFAULT_MAX_FORWARDS;

                forwardedRequest.URI = bridgedDialogue.RemoteTarget;
                forwardedRequest.Header.Routes = bridgedDialogue.RouteSet;
                forwardedRequest.Header.CallId = bridgedDialogue.CallId;
                bridgedDialogue.CSeq = bridgedDialogue.CSeq + 1;
                forwardedRequest.Header.CSeq = bridgedDialogue.CSeq;
                forwardedRequest.Header.To = new SIPToHeader(bridgedDialogue.RemoteUserField.Name, bridgedDialogue.RemoteUserField.URI, bridgedDialogue.RemoteTag);
                forwardedRequest.Header.From = new SIPFromHeader(bridgedDialogue.LocalUserField.Name, bridgedDialogue.LocalUserField.URI, bridgedDialogue.LocalTag);
                forwardedRequest.Header.Contact = new List<SIPContactHeader>() { new SIPContactHeader(null, new SIPURI(bridgedDialogue.RemoteTarget.Scheme, forwardSIPEndPoint)) };
                forwardedRequest.Header.Vias = new SIPViaSet();
                forwardedRequest.Header.Vias.PushViaHeader(new SIPViaHeader(forwardSIPEndPoint, CallProperties.CreateBranchId()));
                forwardedRequest.Header.UserAgent = m_userAgentString;
                forwardedRequest.Header.AuthenticationHeader = null;

                if (inDialogueTransaction.TransactionRequest.Body != null && inDialogueTransaction.TransactionRequest.Method == SIPMethodsEnum.INVITE)
                {
                    bool wasMangled = false;
                    forwardedRequest.Body = SIPPacketMangler.MangleSDP(inDialogueTransaction.TransactionRequest.Body, remoteUAIPAddress.ToString(), out wasMangled);
                    Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Re-INVITE wasmangled=" + wasMangled + " remote=" + remoteUAIPAddress.ToString() + ".", null));
                    forwardedRequest.Header.ContentLength = forwardedRequest.Body.Length;
                }

                SIPEndPoint forwardEndPoint = null;
                if (!bridgedDialogue.ProxySendFrom.IsNullOrBlank())
                {
                    forwardedRequest.Header.ProxySendFrom = bridgedDialogue.ProxySendFrom;
                    // The proxy will always be listening on UDP port 5060 for requests from internal servers.
                    forwardEndPoint = new SIPEndPoint(SIPProtocolsEnum.udp, new IPEndPoint(SIPEndPoint.ParseSIPEndPoint(bridgedDialogue.ProxySendFrom).Address, m_defaultSIPPort));
                }
                else
                {
                    SIPDNSLookupResult lookupResult = m_sipTransport.GetRequestEndPoint(forwardedRequest, m_outboundProxy, false);
                    if (lookupResult.LookupError != null)
                    {
                        logger.Warn("ForwardInDialogueRequest Failed to resolve " + lookupResult.URI.Host + ".");
                    }
                    else
                    {
                        forwardEndPoint = lookupResult.GetSIPEndPoint();
                    }
                }

                if (forwardEndPoint != null)
                {
                    if (inDialogueTransaction.TransactionRequest.Method == SIPMethodsEnum.INVITE)
                    {
                        UACInviteTransaction forwardedTransaction = m_sipTransport.CreateUACTransaction(forwardedRequest, forwardEndPoint, localSIPEndPoint, m_outboundProxy);
                        forwardedTransaction.CDR = null;    // Don't want CDR's on re-INVITES.
                        forwardedTransaction.UACInviteTransactionFinalResponseReceived += InDialogueTransactionFinalResponseReceived;
                        forwardedTransaction.UACInviteTransactionInformationResponseReceived += InDialogueTransactionInfoResponseReceived;
                        forwardedTransaction.TransactionRemoved += InDialogueTransactionRemoved;

                        Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Forwarding re-INVITE from " + remoteEndPoint + " to " + forwardedRequest.URI.ToString() + ", first hop " + forwardEndPoint + ".", dialogue.Owner));

                        forwardedTransaction.SendReliableRequest();

                        lock (m_inDialogueTransactions)
                        {
                            m_inDialogueTransactions.Add(forwardedTransaction.TransactionId, inDialogueTransaction.TransactionId);
                        }
                    }
                    else
                    {
                        SIPNonInviteTransaction forwardedTransaction = m_sipTransport.CreateNonInviteTransaction(forwardedRequest, forwardEndPoint, localSIPEndPoint, m_outboundProxy);
                        forwardedTransaction.NonInviteTransactionFinalResponseReceived += InDialogueTransactionFinalResponseReceived;
                        forwardedTransaction.NonInviteTransactionInfoResponseReceived += InDialogueTransactionInfoResponseReceived;
                        forwardedTransaction.TransactionRemoved += InDialogueTransactionRemoved;

                        Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Forwarding in dialogue " + forwardedRequest.Method + " from " + remoteEndPoint + " to " + forwardedRequest.URI.ToString() + ", first hop " + forwardEndPoint + ".", dialogue.Owner));

                        forwardedTransaction.SendReliableRequest();

                        lock (m_inDialogueTransactions)
                        {
                            m_inDialogueTransactions.Add(forwardedTransaction.TransactionId, inDialogueTransaction.TransactionId);
                        }
                    }

                    // Update the dialogues CSeqs so future in dialogue requests can be forwarded correctly.
                    m_sipDialoguePersistor.UpdateProperty(bridgedDialogue.Id, "CSeq", bridgedDialogue.CSeq);
                    m_sipDialoguePersistor.UpdateProperty(dialogue.Id, "CSeq", dialogue.CSeq);
                }
                else
                {
                    Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Could not forward in dialogue request end point could not be determined " + forwardedRequest.URI.ToString() + ".", dialogue.Owner));
                }
            }
            catch (Exception excp)
            {
                Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.Error, "Exception forwarding in dialogue request. " + excp.Message, dialogue.Owner));
            }
        }
        private void SendNotifyRequestForRefer(SIPRequest referRequest, SIPDialogue referDialogue, SIPEndPoint localEndPoint, SIPResponseStatusCodesEnum responseCode, string responseReason)
        {
            try
            {
                //logger.Debug("Sending NOTIFY for refer subscription to " + referDialogue.RemoteTarget.ToParameterlessString() + ", status " + responseCode + " " + responseReason + ".");

                referDialogue.CSeq++;
                m_sipDialoguePersistor.UpdateProperty(referDialogue.Id, "CSeq", referDialogue.CSeq);

                SIPRequest notifyTryingRequest = GetNotifyRequest(referRequest, referDialogue, new SIPResponse(responseCode, responseReason, null), localEndPoint);
                SIPEndPoint forwardEndPoint = null;
                SIPDNSLookupResult lookupResult = m_sipTransport.GetRequestEndPoint(notifyTryingRequest, m_outboundProxy, false);
                if (lookupResult.LookupError != null)
                {
                    logger.Warn("SendNotifyRequestForRefer Failed to resolve " + lookupResult.URI.Host + ".");
                }
                else
                {
                    forwardEndPoint = lookupResult.GetSIPEndPoint();
                }

                SIPNonInviteTransaction notifyTryingTransaction = m_sipTransport.CreateNonInviteTransaction(notifyTryingRequest, forwardEndPoint, localEndPoint, m_outboundProxy);
                notifyTryingTransaction.SendReliableRequest();
            }
            catch (Exception excp)
            {
                logger.Error("Exception SendNotifyRequestForRefer. " + excp.Message);
            }
        }
        private SIPRequest GetInviteRequest(SIPDialogue dialogue, SIPEndPoint localSIPEndPoint, string body)
        {
            SIPRequest inviteRequest = new SIPRequest(SIPMethodsEnum.INVITE, dialogue.RemoteTarget);

            SIPHeader inviteHeader = new SIPHeader(SIPFromHeader.ParseFromHeader(dialogue.LocalUserField.ToString()), SIPToHeader.ParseToHeader(dialogue.RemoteUserField.ToString()), dialogue.CSeq, dialogue.CallId);
            SIPURI contactURI = new SIPURI(dialogue.RemoteTarget.Scheme, localSIPEndPoint);
            inviteHeader.Contact = SIPContactHeader.ParseContactHeader("<" + contactURI.ToString() + ">");
            inviteHeader.CSeqMethod = SIPMethodsEnum.INVITE;
            inviteRequest.Header = inviteHeader;
            inviteRequest.Header.Routes = dialogue.RouteSet;

            SIPViaHeader viaHeader = new SIPViaHeader(localSIPEndPoint, CallProperties.CreateBranchId());
            inviteRequest.Header.Vias.PushViaHeader(viaHeader);

            inviteRequest.Body = body;
            inviteRequest.Header.ContentLength = body.Length;
            inviteRequest.Header.ContentType = "application/sdp";

            return inviteRequest;
        }
        public void CreateDialogueBridge(SIPDialogue clientDiaglogue, SIPDialogue forwardedDialogue, string owner)
        {
            logger.Debug("Creating dialogue bridge between " + clientDiaglogue.DialogueName + " and " + forwardedDialogue.DialogueName + ".");

            Guid bridgeId = Guid.NewGuid();
            clientDiaglogue.BridgeId = bridgeId;
            forwardedDialogue.BridgeId = bridgeId;

            m_sipDialoguePersistor.Add(new SIPDialogueAsset(clientDiaglogue));
            m_sipDialoguePersistor.Add(new SIPDialogueAsset(forwardedDialogue));

            SIPEndPoint clientDialogueRemoteEP = (IPSocket.IsIPSocket(clientDiaglogue.RemoteTarget.Host)) ? SIPEndPoint.ParseSIPEndPoint(clientDiaglogue.RemoteTarget.Host) : null;
            Log_External(new SIPMonitorMachineEvent(SIPMonitorMachineEventTypesEnum.SIPDialogueCreated, clientDiaglogue.Owner, clientDiaglogue.Id.ToString(), clientDiaglogue.LocalUserField.URI));

            SIPEndPoint forwardedDialogueRemoteEP = (IPSocket.IsIPSocket(forwardedDialogue.RemoteTarget.Host)) ? SIPEndPoint.ParseSIPEndPoint(forwardedDialogue.RemoteTarget.Host) : null;
            Log_External(new SIPMonitorMachineEvent(SIPMonitorMachineEventTypesEnum.SIPDialogueCreated, forwardedDialogue.Owner, forwardedDialogue.Id.ToString(), forwardedDialogue.LocalUserField.URI));
        }
示例#18
0
        /// <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>
        /// Performs transfer between 3 established dialogues (answered calls). The dead dialogue is being replaced by 
        /// the answered dialogue such that a bridged call between the dead and orphaned dialogues now becomes one between the
        /// orphaned and answered dialogues.
        /// </summary>
        /// <param name="deadDialogue">The dialogue that will be terminated.</param>
        /// <param name="orphanedDialogue">The opposite side of the dead dialogue that will be bridged with the answered dialogue.</param>
        /// <param name="answeredDialogue">The newly answered dialogue that will be bridged with the orpahned dialogue.</param>
        public void DialogueTransfer(SIPDialogue deadDialogue, SIPDialogue orphanedDialogue, SIPDialogue answeredDialogue)
        {
            try
            {
                //logger.Debug("SIPDialogueManager DialogueTransfer.");

                // Create bridge between answered dialogue and other end of dialogue being replaced.
                Guid newBridgeId = Guid.NewGuid();
                orphanedDialogue.BridgeId = newBridgeId;
                answeredDialogue.BridgeId = newBridgeId;
                m_sipDialoguePersistor.Update(new SIPDialogueAsset(orphanedDialogue));
                m_sipDialoguePersistor.Add(new SIPDialogueAsset(answeredDialogue));

                Log_External(new SIPMonitorMachineEvent(SIPMonitorMachineEventTypesEnum.SIPDialogueCreated, answeredDialogue.Owner, answeredDialogue.Id.ToString(), answeredDialogue.LocalUserField.URI));
                Log_External(new SIPMonitorMachineEvent(SIPMonitorMachineEventTypesEnum.SIPDialogueUpdated, orphanedDialogue.Owner, orphanedDialogue.Id.ToString(), orphanedDialogue.LocalUserField.URI));

                //logger.Debug("Hanging up dead dialogue");
                // Hangup dialogue being replaced.
                // Check if the dead dialogue has already been hungup. For blind transfers the remote end will usually hangup once it gets a NOTIFY request
                // indicating the transfer is in progress.
                SIPDialogueAsset deadDialogueAsset = m_sipDialoguePersistor.Get(deadDialogue.Id);
                if (deadDialogueAsset != null)
                {
                    deadDialogueAsset.SIPDialogue.Hangup(m_sipTransport, m_outboundProxy);
                    CallHungup(deadDialogue, "Blind transfer", false);
                }
                //logger.Debug("Reinviting two remaining dialogues");
                // Reinvite  other end of dialogue being replaced to answered dialogue.
                ReInvite(orphanedDialogue, answeredDialogue);
                //ReInvite(answeredDialogue, orphanedDialogue.SDP);
            }
            catch (Exception excp)
            {
                logger.Error("Exception DialogueTransfer. " + excp.Message);
            }
        }
示例#20
0
 public void ReInvite(SIPDialogue dialogue, SIPDialogue substituteDialogue)
 {
     m_sipDialogueManager.ReInvite(dialogue, substituteDialogue);
 }
 /// <summary>
 /// Retrieves the other end of a call given the dialogue from one end.
 /// </summary>
 /// <param name="dialogue"></param>
 /// <returns></returns>
 public SIPDialogue GetOppositeDialogue(SIPDialogue dialogue)
 {
     if (dialogue.BridgeId != Guid.Empty)
     {
         string bridgeIdString = dialogue.BridgeId.ToString();
         SIPDialogueAsset dialogueAsset = m_sipDialoguePersistor.Get(d => d.BridgeId == bridgeIdString && d.Id != dialogue.Id);
         return (dialogueAsset != null) ? dialogueAsset.SIPDialogue : null;
     }
     else
     {
         return null;
     }
 }
        public void CallAnswered(SIPResponseStatusCodesEnum answeredStatus, string reasonPhrase, string toTag, string[] customHeaders, string answeredContentType, string answeredBody, SIPDialogue answeredDialogue, SIPDialogueTransferModesEnum uasTransferMode)
        {
            try
            {
                if (!m_isAnswered)
                {
                    m_isAnswered = true;
                    Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Answering client call with a response status of " + (int)answeredStatus + ".", Owner));

                    SIPDialogue uasDialogue = m_sipServerUserAgent.Answer(answeredContentType, answeredBody, toTag, answeredDialogue, uasTransferMode);

                    if (!m_sipServerUserAgent.IsB2B && answeredDialogue != null)
                    {
                        if (uasDialogue != null)
                        {
                            // Duplicate switchboard dialogue settings.
                            uasDialogue.SwitchboardDescription = answeredDialogue.SwitchboardDescription;
                            uasDialogue.SwitchboardCallerDescription = answeredDialogue.SwitchboardCallerDescription;
                            uasDialogue.SwitchboardOwner = answeredDialogue.SwitchboardOwner;

                            // Record the now established call with the call manager for in dialogue management and hangups.
                            CreateBridge_External(uasDialogue, answeredDialogue, m_dialPlan.Owner);
                        }
                        else
                        {
                            logger.Warn("Failed to get a SIPDialogue from UAS.Answer.");
                        }
                    }
                }
                else
                {
                    logger.Warn("DialPlanContext CallAnswered fired on already answered call.");
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception DialPlanContext CallAnswered. " + excp.Message);
            }
            finally
            {
                DialPlanExecutionFinished();
            }
        }
        public void ProcessInDialogueReferRequest(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPRequest sipRequest, SIPDialogue dialogue, Func<string, SIPURI, string, SIPDialogue, ISIPServerUserAgent> blindTransfer)
        {
            try
            {
                Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "REFER received on dialogue " + dialogue.DialogueName + ", transfer mode is " + dialogue.TransferMode + ".", dialogue.Owner));

                SIPNonInviteTransaction referTransaction = m_sipTransport.CreateNonInviteTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, m_outboundProxy);

                if (sipRequest.Header.ReferTo.IsNullOrBlank())
                {
                    // A REFER request must have a Refer-To header.
                    Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Bad REFER request, no Refer-To header.", dialogue.Owner));
                    SIPResponse invalidResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.BadRequest, "Missing mandatory Refer-To header");
                    referTransaction.SendFinalResponse(invalidResponse);
                }
                else
                {
                    if (dialogue.TransferMode == SIPDialogueTransferModesEnum.NotAllowed)
                    {
                        Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "REFER rejected due to dialogue permissions.", dialogue.Owner));
                        SIPResponse declineTransferResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Decline, "Transfers are disabled on dialogue");
                        referTransaction.SendFinalResponse(declineTransferResponse);
                    }
                    else if (Regex.Match(sipRequest.Header.ReferTo, m_referReplacesParameter).Success)
                    {
                        // Attended transfers are allowed unless explicitly blocked. Attended transfers are not dangerous
                        // as no new call is created and it's the same as a re-invite.
                        if (dialogue.TransferMode == SIPDialogueTransferModesEnum.PassThru)
                        {
                            Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "REFER received, attended transfer, passing through, Referred-By=" + sipRequest.Header.ReferredBy + ".", dialogue.Owner));
                            ForwardInDialogueRequest(dialogue, referTransaction, localSIPEndPoint, remoteEndPoint);
                        }
                        else
                        {
                            Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "REFER received, attended transfer, processing on app server, Referred-By=" + sipRequest.Header.ReferredBy + ".", dialogue.Owner));
                            ProcessAttendedRefer(dialogue, referTransaction, sipRequest, localSIPEndPoint, remoteEndPoint);
                        }
                    }
                    else
                    {
                        bool referAuthenticated = false;
                        if (dialogue.TransferMode == SIPDialogueTransferModesEnum.Default)
                        {
                            string canonicalDomain = GetCanonicalDomain_External(sipRequest.Header.From.FromURI.Host, false);
                            if (!canonicalDomain.IsNullOrBlank())
                            {
                                referAuthenticated = AuthenticateReferRequest(referTransaction, sipRequest.Header.From.FromURI.User, canonicalDomain);
                            }
                        }

                        if (dialogue.TransferMode == SIPDialogueTransferModesEnum.BlindPlaceCall || referAuthenticated)
                        {
                            // A blind transfer that is permitted to initiate a new call.
                            //logger.Debug("Blind Transfer starting.");
                            SIPResponse acceptedResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Accepted, null);
                            referTransaction.SendFinalResponse(acceptedResponse);
                            SendNotifyRequestForRefer(sipRequest, dialogue, localSIPEndPoint, SIPResponseStatusCodesEnum.Trying, null);
                            //SIPDialogue oppositeDialogue = GetOppositeDialogue(dialogue);
                            SIPUserField replacesUserField = SIPUserField.ParseSIPUserField(sipRequest.Header.ReferTo);
                            ISIPServerUserAgent transferUAS = blindTransfer(dialogue.Owner, replacesUserField.URI, "transfer", dialogue);
                            bool sendNotifications = true;
                            Guid originalBridgeID = dialogue.BridgeId;
                            transferUAS.UASStateChanged += (uas, status, reason) =>
                            {
                                if (sendNotifications)
                                {
                                    if (status != SIPResponseStatusCodesEnum.Trying)
                                    {
                                        // As soon as a blind transfer receives a non-100 response break the bridge as most UA's will immediately hangup the call once
                                        // they are informed it's proceeding.
                                        if (dialogue.BridgeId != Guid.Empty)
                                        {
                                            dialogue.BridgeId = Guid.Empty;
                                            m_sipDialoguePersistor.UpdateProperty(dialogue.Id, "BridgeId", dialogue.BridgeId.ToString());
                                        }

                                        // Retrieve the dialogue anew each time a new response is received in order to check if it still exists.
                                        SIPDialogueAsset updatedDialogue = m_sipDialoguePersistor.Get(dialogue.Id);
                                        if (updatedDialogue != null)
                                        {
                                            SendNotifyRequestForRefer(sipRequest, updatedDialogue.SIPDialogue, localSIPEndPoint, status, reason);
                                        }
                                        else
                                        {
                                            // The dialogue the blind transfer notifications were being sent on has been hungup no point sending any more notifications.
                                            sendNotifications = false;
                                        }
                                    }
                                }

                                if ((int)status >= 400)
                                {
                                    // The transfer has failed. Attempt to re-bridge if possible or if not hangup the orphaned end of the call.
                                    SIPDialogueAsset referDialogue = m_sipDialoguePersistor.Get(dialogue.Id);   // Dialogue that initiated the REFER.
                                    if (referDialogue != null)
                                    {
                                        Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Blind transfer to " + replacesUserField.URI.ToParameterlessString() + " failed with " + status + ", the initiating dialogue is still available re-creating the original bridge.", dialogue.Owner));
                                        // Re-bridging the two original dialogues.
                                        m_sipDialoguePersistor.UpdateProperty(dialogue.Id, "BridgeId", originalBridgeID.ToString());
                                    }
                                    else
                                    {
                                        Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Blind transfer to " + replacesUserField.URI.ToParameterlessString() + " failed with " + status + ", the initiating dialogue hungup, hanging up remaining dialogue.", dialogue.Owner));
                                        // The transfer failed and the dialogue that initiated the transfer has hungup. No point keeping the other end up so hang it up as well.
                                        string bridgeIDStr = originalBridgeID.ToString();
                                        SIPDialogueAsset orphanedDialogueAsset = m_sipDialoguePersistor.Get(d => d.BridgeId == bridgeIDStr);
                                        if (orphanedDialogueAsset != null)
                                        {
                                            HangupDialogue(orphanedDialogueAsset.SIPDialogue, "Blind transfer failed and remote end already hungup.", true);
                                        }
                                    }
                                }
                            };
                            //logger.Debug("Blind Transfer successfully initated, dial plan processing now in progress.");
                        }
                        else
                        {
                            Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "REFER received, blind transfer, Refer-To=" + sipRequest.Header.ReferTo + ", Referred-By=" + sipRequest.Header.ReferredBy + ".", dialogue.Owner));
                            //SIPNonInviteTransaction passThruTransaction = m_sipTransport.CreateNonInviteTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, m_outboundProxy);
                            ForwardInDialogueRequest(dialogue, referTransaction, localSIPEndPoint, remoteEndPoint);
                        }
                    }
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception ProcessInDialogueReferRequest. " + excp.Message);
                throw;
            }
        }
 public SIPDialogue Answer(string contentType, string body, SIPDialogue answeredDialogue, SIPDialogueTransferModesEnum transferMode)
 {
     return Answer(contentType, body, null, answeredDialogue, transferMode);
 }
        public void ProcessInDialogueRequest(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPRequest sipRequest, SIPDialogue dialogue)
        {
            try
            {
                if (sipRequest.Method == SIPMethodsEnum.BYE)
                {
                    SIPNonInviteTransaction byeTransaction = m_sipTransport.CreateNonInviteTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, m_outboundProxy);
                    //logger.Debug("Matching dialogue found for BYE request to " + sipRequest.URI.ToString() + ".");
                    SIPResponse byeResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null);
                    byeTransaction.SendFinalResponse(byeResponse);

                    string hangupReason = sipRequest.Header.Reason;
                    if (hangupReason.IsNullOrBlank())
                    {
                        hangupReason = sipRequest.Header.GetUnknownHeaderValue("X-Asterisk-HangupCause");
                    }

                    if (sipRequest.Header.SwitchboardTerminate == "both")
                    {
                        // BYE request from switchboard that's requesting two dialogues to be hungup by the server.
                        CallHungup(dialogue, hangupReason, true);
                    }
                    else
                    {
                        // Normal BYE request.
                        CallHungup(dialogue, hangupReason, false);
                    }
                }
                else if (sipRequest.Method == SIPMethodsEnum.INVITE)
                {
                    UASInviteTransaction reInviteTransaction = m_sipTransport.CreateUASTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, m_outboundProxy);
                    SIPResponse tryingResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Trying, null);
                    reInviteTransaction.SendInformationalResponse(tryingResponse);
                    reInviteTransaction.CDR = null;     // Don't want CDR's on re-INVITEs.
                    ForwardInDialogueRequest(dialogue, reInviteTransaction, localSIPEndPoint, remoteEndPoint);
                }
                else if (sipRequest.Method == SIPMethodsEnum.OPTIONS)
                {
                    // Send back the remote SDP.
                    Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "OPTIONS request for established dialogue " + dialogue.DialogueName + ".", dialogue.Owner));
                    SIPNonInviteTransaction optionsTransaction = m_sipTransport.CreateNonInviteTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, m_outboundProxy);
                    SIPResponse okResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null);
                    okResponse.Body = dialogue.RemoteSDP;
                    okResponse.Header.ContentLength = okResponse.Body.Length;
                    okResponse.Header.ContentType = m_sdpContentType;
                    optionsTransaction.SendFinalResponse(okResponse);
                }
                else if (sipRequest.Method == SIPMethodsEnum.MESSAGE)
                {
                    Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "MESSAGE for call " + sipRequest.URI.ToString() + ": " + sipRequest.Body + ".", dialogue.Owner));
                    SIPNonInviteTransaction messageTransaction = m_sipTransport.CreateNonInviteTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, m_outboundProxy);
                    SIPResponse okResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null);
                    messageTransaction.SendFinalResponse(okResponse);
                }
                else
                {
                    // This is a request on an established call forward through to the opposite dialogue.
                    SIPNonInviteTransaction passThruTransaction = m_sipTransport.CreateNonInviteTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, m_outboundProxy);
                    ForwardInDialogueRequest(dialogue, passThruTransaction, localSIPEndPoint, remoteEndPoint);
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception ProcessInDialogueRequest. " + excp.Message);
                throw;
            }
        }
示例#26
0
        private void UACCallAnswered(ISIPClientUserAgent answeredUAC, SIPResponse answeredResponse)
        {
            try
            {
                // Remove the current call from the pending list.
                lock (m_switchCalls)
                {
                    m_switchCalls.Remove(answeredUAC);
                }

                if (m_switchCallTransactions != null && answeredUAC.ServerTransaction != null)
                {
                    m_switchCallTransactions.Add(answeredUAC.ServerTransaction);
                }

                if (answeredResponse != null && answeredResponse.StatusCode >= 200 && answeredResponse.StatusCode <= 299)
                {
                    #region 2xx final response.

                    if (!m_callAnswered && !m_commandCancelled)
                    {
                        // This is the first call we've got an answer on.
                        m_callAnswered = true;
                        m_answeredUAC = answeredUAC;
                        AnsweredSIPResponse = answeredResponse;

                        SIPDialogueTransferModesEnum uasTransferMode = SIPDialogueTransferModesEnum.Default;
                        if (m_answeredUAC.CallDescriptor.TransferMode == SIPDialogueTransferModesEnum.NotAllowed)
                        {
                            answeredUAC.SIPDialogue.TransferMode = SIPDialogueTransferModesEnum.NotAllowed;
                            uasTransferMode = SIPDialogueTransferModesEnum.NotAllowed;
                        }
                        else if (m_answeredUAC.CallDescriptor.TransferMode == SIPDialogueTransferModesEnum.BlindPlaceCall)
                        {
                            answeredUAC.SIPDialogue.TransferMode = SIPDialogueTransferModesEnum.BlindPlaceCall;
                            uasTransferMode = SIPDialogueTransferModesEnum.BlindPlaceCall;
                        }
                        else if (m_answeredUAC.CallDescriptor.TransferMode == SIPDialogueTransferModesEnum.PassThru)
                        {
                            answeredUAC.SIPDialogue.TransferMode = SIPDialogueTransferModesEnum.PassThru;
                            uasTransferMode = SIPDialogueTransferModesEnum.PassThru;
                        }
                        /*else if (m_answeredUAC.CallDescriptor.TransferMode == SIPCallTransferModesEnum.Caller)
                        {
                            answeredUAC.SIPDialogue.TransferMode = SIPDialogueTransferModesEnum.NotAllowed;
                            uasTransferMode = SIPDialogueTransferModesEnum.Allowed;
                        }
                        else if (m_answeredUAC.CallDescriptor.TransferMode == SIPCallTransferModesEnum.Callee)
                        {
                            answeredUAC.SIPDialogue.TransferMode = SIPDialogueTransferModesEnum.Allowed;
                            uasTransferMode = SIPDialogueTransferModesEnum.NotAllowed;
                        }
                        else if (m_answeredUAC.CallDescriptor.TransferMode == SIPCallTransferModesEnum.Both)
                        {
                            answeredUAC.SIPDialogue.TransferMode = SIPDialogueTransferModesEnum.Allowed;
                            uasTransferMode = SIPDialogueTransferModesEnum.Allowed;
                        }*/

                        if (CallAnswered != null)
                        {
                            logger.Debug("Transfer mode=" + m_answeredUAC.CallDescriptor.TransferMode + ".");
                            CallAnswered(answeredResponse.Status, answeredResponse.ReasonPhrase, null, null, answeredResponse.Header.ContentType, answeredResponse.Body, answeredUAC.SIPDialogue, uasTransferMode);
                        }

                        // Cancel/hangup and other calls on this leg that are still around.
                        CancelNotRequiredCallLegs(CallCancelCause.NormalClearing);
                    }
                    else
                    {
                        // Call already answered or cancelled, hangup (send BYE).
                        FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Call leg " + answeredUAC.CallDescriptor.Uri + " answered but call was already answered or cancelled, hanging up.", m_username));
                        SIPDialogue sipDialogue = new SIPDialogue(answeredUAC.ServerTransaction, m_username, m_adminMemberId);
                        sipDialogue.Hangup(m_sipTransport, m_outboundProxySocket);
                    }

                    #endregion

                    CallLegCompleted();
                }
                else if (answeredUAC.SIPDialogue != null)
                {
                    // Google Voice calls create the dialogue without using a SIP response.
                    if (!m_callAnswered && !m_commandCancelled)
                    {
                        FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Call leg for Google Voice call to " + answeredUAC.CallDescriptor.Uri + " answered.", m_username));

                        // This is the first call we've got an answer on.
                        m_callAnswered = true;
                        m_answeredUAC = answeredUAC;

                        if (CallAnswered != null)
                        {
                            CallAnswered(SIPResponseStatusCodesEnum.Ok, null, null, null, answeredUAC.SIPDialogue.ContentType, answeredUAC.SIPDialogue.RemoteSDP, answeredUAC.SIPDialogue, SIPDialogueTransferModesEnum.NotAllowed);
                        }

                        // Cancel/hangup and other calls on this leg that are still around.
                        CancelNotRequiredCallLegs(CallCancelCause.NormalClearing);
                    }
                    else
                    {
                        // Call already answered or cancelled, hangup (send BYE).
                        FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Call leg for Google Voice call to " + answeredUAC.CallDescriptor.Uri + " answered but call was already answered or cancelled, hanging up.", m_username));
                        answeredUAC.SIPDialogue.Hangup(m_sipTransport, m_outboundProxySocket);
                    }
                }
                else if (answeredResponse != null && answeredResponse.StatusCode >= 300 && answeredResponse.StatusCode <= 399)
                {
                    ProcessRedirect(answeredUAC, answeredResponse);
                }
                else if (answeredResponse != null)
                {
                    // This call leg failed, record the failure status and reason.
                    m_lastFailureStatus = answeredResponse.Status;
                    m_lastFailureReason = answeredResponse.ReasonPhrase;

                    if (m_switchCallTransactions != null && answeredUAC.ServerTransaction != null)
                    {
                        m_switchCallTransactions.Add(answeredUAC.ServerTransaction);
                    }

                    CallLegCompleted();
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception ForkCall UACCallAnswered. " + excp);
            }
        }
        public string SubscribeClient(
            string owner,
            string adminID,
            SIPRequest subscribeRequest,
            string toTag,
            SIPURI canonicalResourceURI,
            out SIPResponseStatusCodesEnum errorResponse,
            out string errorReason)
        {
            try
            {
                errorResponse = SIPResponseStatusCodesEnum.None;
                errorReason = null;

                SIPURI resourceURI = subscribeRequest.URI.CopyOf();
                SIPEventPackage eventPackage = SIPEventPackage.Parse(subscribeRequest.Header.Event);
                int expiry = subscribeRequest.Header.Expires;

                if (!(eventPackage == SIPEventPackage.Dialog || eventPackage == SIPEventPackage.Presence))
                {
                    throw new ApplicationException("Event package " + eventPackage.ToString() + " is not supported by the subscriptions manager.");
                }
                else
                {
                    if (expiry > 0)
                    {
                        string subscribeError = null;
                        string sessionID = Guid.NewGuid().ToString();
                        SIPDialogue subscribeDialogue = new SIPDialogue(subscribeRequest, owner, adminID, toTag);

                        if (eventPackage == SIPEventPackage.Dialog)
                        {
                            string monitorFilter = "dialog " + canonicalResourceURI.ToString();
                            if (!subscribeRequest.Body.IsNullOrBlank())
                            {
                                monitorFilter += " and " + subscribeRequest.Body;
                            }

                            m_publisher.Subscribe(owner, adminID, m_notificationsAddress, sessionID, SIPMonitorClientTypesEnum.Machine.ToString(), monitorFilter, expiry, null, out subscribeError);

                            if (subscribeError != null)
                            {
                                throw new ApplicationException(subscribeError);
                            }
                            else
                            {
                                SIPDialogEventSubscription subscription = new SIPDialogEventSubscription(MonitorLogEvent_External, sessionID, resourceURI, canonicalResourceURI, monitorFilter, subscribeDialogue, expiry, GetDialogues_External, GetDialogue_External);
                                m_subscriptions.Add(sessionID, subscription);
                                MonitorLogEvent_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.Notifier, SIPMonitorEventTypesEnum.SubscribeAccept, "New dialog subscription created for " + resourceURI.ToString() + ", expiry " + expiry + "s.", owner));
                            }
                        }
                        else if (eventPackage == SIPEventPackage.Presence)
                        {
                            string monitorFilter = "presence " + canonicalResourceURI.ToString();
                            m_publisher.Subscribe(owner, adminID, m_notificationsAddress, sessionID, SIPMonitorClientTypesEnum.Machine.ToString(), monitorFilter, expiry, null, out subscribeError);

                            if (subscribeError != null)
                            {
                                throw new ApplicationException(subscribeError);
                            }
                            else
                            {
                                bool switchboardAccountsOnly = subscribeRequest.Body == SIPPresenceEventSubscription.SWITCHBOARD_FILTER;
                                SIPPresenceEventSubscription subscription = new SIPPresenceEventSubscription(MonitorLogEvent_External, sessionID, resourceURI, canonicalResourceURI, monitorFilter, subscribeDialogue, expiry, m_sipAssetPersistor, GetSIPRegistrarBindingsCount_External, switchboardAccountsOnly);
                                m_subscriptions.Add(sessionID, subscription);
                                MonitorLogEvent_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.Notifier, SIPMonitorEventTypesEnum.SubscribeAccept, "New presence subscription created for " + resourceURI.ToString() + ", expiry " + expiry + "s.", owner));
                            }
                        }

                        return sessionID;
                    }

                    return null;
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception NotifierSubscriptionsManager SubscribeClient. " + excp.Message);
                throw;
            }
        }