/// <summary> /// In transaction ACK requests are for non-2xx responses, i.e. INVITE rejected and no dialogue being created. /// </summary> private SIPRequest GetInTransactionACKRequest(SIPResponse sipResponse, SIPURI ackURI, SIPEndPoint localSIPEndPoint) { SIPRequest ackRequest = new SIPRequest(SIPMethodsEnum.ACK, ackURI.ToString()); ackRequest.LocalSIPEndPoint = localSIPEndPoint; SIPHeader header = new SIPHeader(TransactionRequest.Header.From, sipResponse.Header.To, sipResponse.Header.CSeq, sipResponse.Header.CallId); header.CSeqMethod = SIPMethodsEnum.ACK; header.AuthenticationHeader = TransactionRequest.Header.AuthenticationHeader; header.Routes = base.TransactionRequest.Header.Routes; header.ProxySendFrom = base.TransactionRequest.Header.ProxySendFrom; ackRequest.Header = header; SIPViaHeader viaHeader = new SIPViaHeader(localSIPEndPoint, sipResponse.Header.Vias.TopViaHeader.Branch); ackRequest.Header.Vias.PushViaHeader(viaHeader); return ackRequest; }
/// <summary> /// New transaction ACK requests are for 2xx responses, i.e. INVITE accepted and dialogue being created. /// </summary> /// <remarks> /// From RFC 3261 Chapter 17.1.1.3 - ACK for non-2xx final responses /// /// IMPORTANT: /// an ACK for a non-2xx response will also have the same branch ID as the INVITE whose response it acknowledges. /// /// The ACK request constructed by the client transaction MUST contain /// values for the Call-ID, From, and Request-URI that are equal to the /// values of those header fields in the request passed to the transport /// by the client transaction (call this the "original request"). The To /// header field in the ACK MUST equal the To header field in the /// response being acknowledged, and therefore will usually differ from /// the To header field in the original request by the addition of the /// tag parameter. The ACK MUST contain a single Via header field, and /// this MUST be equal to the top Via header field of the original /// request. The CSeq header field in the ACK MUST contain the same /// value for the sequence number as was present in the original request, /// but the method parameter MUST be equal to "ACK". /// /// If the INVITE request whose response is being acknowledged had Route /// header fields, those header fields MUST appear in the ACK. This is /// to ensure that the ACK can be routed properly through any downstream /// stateless proxies. /// /// From RFC 3261 Chapter 13.2.2.4 - ACK for 2xx final responses /// /// IMPORTANT: /// an ACK for a 2xx final response is a new transaction and has a new branch ID. /// /// The UAC core MUST generate an ACK request for each 2xx received from /// the transaction layer. The header fields of the ACK are constructed /// in the same way as for any request sent within a dialog (see Section /// 12) with the exception of the CSeq and the header fields related to /// authentication. The sequence number of the CSeq header field MUST be /// the same as the INVITE being acknowledged, but the CSeq method MUST /// be ACK. The ACK MUST contain the same credentials as the INVITE. If /// the 2xx contains an offer (based on the rules above), the ACK MUST /// carry an answer in its body. If the offer in the 2xx response is not /// acceptable, the UAC core MUST generate a valid answer in the ACK and /// then send a BYE immediately. /// </remarks> private SIPRequest GetNewTransactionACKRequest(SIPResponse sipResponse, SIPURI ackURI, SIPEndPoint localSIPEndPoint) { SIPRequest ackRequest = new SIPRequest(SIPMethodsEnum.ACK, ackURI.ToString()); ackRequest.LocalSIPEndPoint = localSIPEndPoint; SIPHeader header = new SIPHeader(TransactionRequest.Header.From, sipResponse.Header.To, sipResponse.Header.CSeq, sipResponse.Header.CallId); header.CSeqMethod = SIPMethodsEnum.ACK; header.AuthenticationHeader = TransactionRequest.Header.AuthenticationHeader; header.ProxySendFrom = base.TransactionRequest.Header.ProxySendFrom; if (sipResponse.Header.RecordRoutes != null) { header.Routes = sipResponse.Header.RecordRoutes.Reversed(); } ackRequest.Header = header; SIPViaHeader viaHeader = new SIPViaHeader(localSIPEndPoint, CallProperties.CreateBranchId()); ackRequest.Header.Vias.PushViaHeader(viaHeader); return ackRequest; }
/// <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> /// Can't be used for local destinations! /// </summary> /// <param name="sipRequest"></param> /// <param name="command"></param> /// <returns></returns> private SIPCallDescriptor GetForwardsForExternalLeg( SIPRequest sipRequest, SIPURI callLegURI, string customContentType, string customContent, string fromDisplayName, string fromUsername, string fromHost, CRMHeaders contact) { try { SIPCallDescriptor sipCallDescriptor = null; Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Attempting to locate a provider for call leg: " + callLegURI.ToString() + ".", m_username)); bool providerFound = false; string contentType = (sipRequest != null) ? sipRequest.Header.ContentType : null; string content = (sipRequest != null) ? sipRequest.Body : null; if (!customContentType.IsNullOrBlank()) { contentType = customContentType; } if (!customContent.IsNullOrBlank()) { content = customContent; } IPAddress publicSDPAddress = PublicIPAddress; if (publicSDPAddress == null && sipRequest != null) { publicSDPAddress = SIPPacketMangler.GetRequestIPAddress(sipRequest); } if (m_sipProviders != null) { foreach (SIPProvider provider in m_sipProviders) { if (callLegURI.Host.ToUpper() == provider.ProviderName.ToUpper()) { if (provider.ProviderType == ProviderTypes.GoogleVoice) { sipCallDescriptor = new SIPCallDescriptor( provider.ProviderUsername, provider.ProviderPassword, callLegURI.User + "@" + provider.ProviderName, provider.GVCallbackNumber, provider.GVCallbackPattern, (provider.GVCallbackType != null) ? (int)provider.GVCallbackType.Value : DEFAULT_GVCALLBACK_TYPE, content, contentType); sipCallDescriptor.CRMHeaders = contact; providerFound = true; break; } else { SIPURI providerURI = SIPURI.ParseSIPURI(provider.ProviderServer); if (providerURI != null) { providerURI.User = callLegURI.User; if (callLegURI.Parameters.Count > 0) { foreach (string parameterName in callLegURI.Parameters.GetKeys()) { if (!providerURI.Parameters.Has(parameterName)) { providerURI.Parameters.Set(parameterName, callLegURI.Parameters.Get(parameterName)); } } } if (callLegURI.Headers.Count > 0) { foreach (string headerName in callLegURI.Headers.GetKeys()) { if (!providerURI.Headers.Has(headerName)) { providerURI.Headers.Set(headerName, callLegURI.Headers.Get(headerName)); } } } SIPFromHeader fromHeader = ParseFromHeaderOption(provider.ProviderFrom, sipRequest, provider.ProviderUsername, providerURI.Host); sipCallDescriptor = new SIPCallDescriptor( provider.ProviderUsername, provider.ProviderPassword, providerURI.ToString(), fromHeader.ToString(), providerURI.ToString(), null, SIPCallDescriptor.ParseCustomHeaders(SubstituteRequestVars(sipRequest, provider.CustomHeaders)), provider.ProviderAuthUsername, SIPCallDirection.Out, contentType, content, publicSDPAddress); sipCallDescriptor.CRMHeaders = contact; if (!provider.ProviderOutboundProxy.IsNullOrBlank()) { sipCallDescriptor.ProxySendFrom = provider.ProviderOutboundProxy.Trim(); } providerFound = true; if (provider.ProviderFrom.IsNullOrBlank()) { // If there is already a custom From header set on the provider that overrides the general settings. sipCallDescriptor.SetGeneralFromHeaderFields(fromDisplayName, fromUsername, fromHost); } break; } else { Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Could not parse SIP URI from Provider Server " + provider.ProviderServer + " in GetForwardsForExternalLeg.", m_username)); } } } } } if (!providerFound) { // Treat as an anonymous SIP URI. // Copy the From header so the tag can be removed before adding to the forwarded request. string fromHeaderStr = (sipRequest != null) ? sipRequest.Header.From.ToString() : m_anonymousFromURI; SIPFromHeader forwardedFromHeader = SIPFromHeader.ParseFromHeader(fromHeaderStr); forwardedFromHeader.FromTag = null; sipCallDescriptor = new SIPCallDescriptor( m_anonymousUsername, null, callLegURI.ToString(), forwardedFromHeader.ToString(), callLegURI.ToString(), null, null, null, SIPCallDirection.Out, contentType, content, publicSDPAddress); sipCallDescriptor.CRMHeaders = contact; sipCallDescriptor.SetGeneralFromHeaderFields(fromDisplayName, fromUsername, fromHost); } return sipCallDescriptor; } catch (Exception excp) { logger.Error("Exception GetForwardsForExternalLeg. " + excp.Message); return null; } }
public List<SIPCallDescriptor> GetForwardsForRedirect(SIPURI redirectURI, SIPCallDescriptor callDescriptor) { Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Getting forwards for redirect to " + redirectURI.ToString() + ".", m_username)); List<SIPCallDescriptor> redirectCalls = new List<SIPCallDescriptor>(); string localDomain = GetCanonicalDomain_External(redirectURI.Host, false); if (localDomain != null) { Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Redirect is for local SIP account " + redirectURI.User + " and canonical domain " + localDomain + ", looking up bindings.", m_username)); SIPAccount sipAccount = GetSIPAccount_External(s => s.SIPUsername == redirectURI.User && s.SIPDomain == localDomain); List<SIPRegistrarBinding> bindings = GetRegistrarBindings_External(b => b.SIPAccountId == sipAccount.Id, null, 0, Int32.MaxValue); if (bindings != null) { Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, bindings.Count + " bindings found for local SIP account " + sipAccount.SIPUsername + "@" + sipAccount.SIPDomain + " when processing redirect.", m_username)); // Build list of registered contacts. for (int index = 0; index < bindings.Count; index++) { SIPRegistrarBinding binding = bindings[index]; SIPURI contactURI = binding.MangledContactSIPURI; SIPCallDescriptor redirectCallDescriptor = callDescriptor.CopyOf(); redirectCallDescriptor.Uri = contactURI.ToString(); redirectCalls.Add(redirectCallDescriptor); } } else { Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "No bindings were found for local SIP account " + sipAccount.SIPUsername + "@" + sipAccount.SIPDomain + " when processing redirect.", m_username)); } } else { Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Redirect destination " + redirectURI.ToString() + " was determined as an external URI.", m_username)); SIPCallDescriptor redirectCallDescriptor = callDescriptor.CopyOf(); redirectCallDescriptor.Uri = redirectURI.ToString(); redirectCalls.Add(redirectCallDescriptor); } return redirectCalls; }
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 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; } }
public string ToXMLText() { XNamespace ns = m_dialogXMLNS; XDocument dialogEventDoc = new XDocument(new XElement(ns + "dialog-info", new XAttribute("version", Version), new XAttribute("state", State), new XAttribute("entity", Entity.ToString()) )); DialogItems.ForEach((item) => { XElement dialogItemElement = item.ToXML(); dialogEventDoc.Root.Add(dialogItemElement); item.HasBeenSent = true; }); StringBuilder sb = new StringBuilder(); XmlWriterSettings xws = new XmlWriterSettings(); xws.NewLineHandling = NewLineHandling.None; xws.Indent = true; using (XmlWriter xw = XmlWriter.Create(sb, xws)) { dialogEventDoc.WriteTo(xw); } return(sb.ToString()); }
public override string ToXMLText() { XNamespace ns = m_pidfXMLNS; XDocument presenceDoc = new XDocument(new XElement(ns + "presence", new XAttribute("entity", Entity.ToString()))); Tuples.ForEach((item) => { XElement tupleElement = item.ToXML(); presenceDoc.Root.Add(tupleElement); }); StringBuilder sb = new StringBuilder(); XmlWriterSettings xws = new XmlWriterSettings(); xws.NewLineHandling = NewLineHandling.None; xws.Indent = true; using (XmlWriter xw = XmlWriter.Create(sb, xws)) { presenceDoc.WriteTo(xw); } return(sb.ToString()); }
/// <summary> /// Puts the dialog participant information to an XML element. /// </summary> /// <param name="nodeName">A participant can represent a local or remote party, the node name needs to be set to either "local" or "remote".</param> /// <returns>An XML element representing the dialog participant.</returns> public XElement ToXML(string nodeName) { XNamespace ns = m_dialogXMLNS; XNamespace ss = m_sipsorceryXMLNS; XElement participantElement = new XElement(ns + nodeName); if (URI != null) { XElement identityElement = new XElement(ns + "identity", URI.ToString()); if (!DisplayName.IsNullOrBlank()) { identityElement.Add(new XAttribute("display-name", DisplayName)); } participantElement.Add(identityElement); } if (TargetURI != null) { XElement targetElement = new XElement(ns + "target", new XAttribute("uri", TargetURI.ToString())); participantElement.Add(targetElement); } if (CSeq > 0) { XElement cseqElement = new XElement(ns + "cseq", CSeq); participantElement.Add(cseqElement); } if (!SwitchboardLineName.IsNullOrBlank()) { XElement switchLineNameElement = new XElement(ss + "switchboardlinename", SwitchboardLineName); participantElement.Add(switchLineNameElement); } if (!CRMPersonName.IsNullOrBlank()) { XElement crmPersonNameElement = new XElement(ss + "crmpersonname", CRMPersonName); participantElement.Add(crmPersonNameElement); } if (!CRMCompanyName.IsNullOrBlank()) { XElement crmCompanyNameElement = new XElement(ss + "crmcompanyname", CRMCompanyName); participantElement.Add(crmCompanyNameElement); } if (!CRMPictureURL.IsNullOrBlank()) { XElement crmPictureURLElement = new XElement(ss + "crmpictureurl", CRMPictureURL); participantElement.Add(crmPictureURLElement); } return(participantElement); }
private SIPRequest GetReferRequest(SIPEndPoint localSIPEndPoint, SIPURI referTo) { SIPRequest referRequest = new SIPRequest(SIPMethodsEnum.REFER, RemoteTarget); SIPFromHeader referFromHeader = SIPFromHeader.ParseFromHeader(LocalUserField.ToString()); SIPToHeader referToHeader = SIPToHeader.ParseToHeader(RemoteUserField.ToString()); int cseq = ++CSeq; SIPHeader referHeader = new SIPHeader(referFromHeader, referToHeader, cseq, CallId); referHeader.CSeqMethod = SIPMethodsEnum.REFER; referRequest.Header = referHeader; referRequest.Header.ReferTo = referTo.ToString(); referRequest.Header.Routes = RouteSet; referRequest.Header.ProxySendFrom = ProxySendFrom; SIPViaHeader viaHeader = new SIPViaHeader(localSIPEndPoint, CallProperties.CreateBranchId()); referRequest.Header.Vias.PushViaHeader(viaHeader); return referRequest; }