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); } }
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); } }
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; } }
/// <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); } }
/// <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)); }
/// <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); } }
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; } }
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; } }