PageRenderTime 31ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/sipsorcery-core/SIPSorcery.Servers.Cores/SIPApplicationServer/SIPDialogueManager.cs

https://github.com/thecc4re/sipsorcery-mono
C# | 1311 lines | 1009 code | 142 blank | 160 comment | 172 complexity | e72e75535177c5c5791f3f3ac374ad96 MD5 | raw file
Possible License(s): CC-BY-SA-3.0
  1. // ============================================================================
  2. // FileName: SIPDialogueManager.cs
  3. //
  4. // Description:
  5. // Manages established dialogues.
  6. //
  7. // Author(s):
  8. // Aaron Clauson
  9. //
  10. // History:
  11. // 10 Feb 2008 Aaron Clauson Created.
  12. //
  13. // License:
  14. // This software is licensed under the BSD License http://www.opensource.org/licenses/bsd-license.php
  15. //
  16. // Copyright (c) 2008 Aaron Clauson (aaronc@blueface.ie), Blue Face Ltd, Dublin, Ireland (www.blueface.ie)
  17. // All rights reserved.
  18. //
  19. // Redistribution and use in source and binary forms, with or without modification, are permitted provided that
  20. // the following conditions are met:
  21. //
  22. // Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  23. // Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
  24. // disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of Blue Face Ltd.
  25. // nor the names of its contributors may be used to endorse or promote products derived from this software without specific
  26. // prior written permission.
  27. //
  28. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
  29. // BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  30. // IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
  31. // OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
  32. // OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  33. // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  34. // POSSIBILITY OF SUCH DAMAGE.
  35. // ============================================================================
  36. using System;
  37. using System.Collections.Generic;
  38. using System.Data;
  39. using System.Data.SqlClient;
  40. using System.Linq;
  41. using System.Net;
  42. using System.Text;
  43. using System.Text.RegularExpressions;
  44. using System.Threading;
  45. using System.Transactions;
  46. using SIPSorcery.AppServer.DialPlan;
  47. using SIPSorcery.CRM;
  48. using SIPSorcery.Net;
  49. using SIPSorcery.Persistence;
  50. using SIPSorcery.SIP;
  51. using SIPSorcery.SIP.App;
  52. using SIPSorcery.Sys;
  53. using log4net;
  54. namespace SIPSorcery.Servers
  55. {
  56. public class SIPDialogueManager : ISIPDialogueManager
  57. {
  58. private static ILog logger = AppState.logger;
  59. private static string m_userAgentString = SIPConstants.SIP_USERAGENT_STRING;
  60. private static string m_remoteHangupCause = SIPConstants.SIP_REMOTEHANGUP_CAUSE;
  61. private static string m_referReplacesParameter = SIPHeaderAncillary.SIP_REFER_REPLACES;
  62. private static string m_referNotifyEventValue = SIPEventPackage.Refer.ToString();
  63. private static string m_referNotifyContentType = SIPMIMETypes.REFER_CONTENT_TYPE;
  64. private static readonly int m_defaultSIPPort = SIPConstants.DEFAULT_SIP_PORT;
  65. private static readonly string m_sdpContentType = SDP.SDP_MIME_CONTENTTYPE;
  66. private SIPMonitorLogDelegate Log_External;
  67. private SIPAuthenticateRequestDelegate SIPAuthenticateRequest_External;
  68. private SIPAssetGetDelegate<SIPAccount> GetSIPAccount_External;
  69. private GetCanonicalDomainDelegate GetCanonicalDomain_External;
  70. private SIPTransport m_sipTransport;
  71. private SIPEndPoint m_outboundProxy;
  72. private SIPAssetPersistor<SIPDialogueAsset> m_sipDialoguePersistor;
  73. private SIPAssetPersistor<SIPCDRAsset> m_sipCDRPersistor;
  74. private Dictionary<string, string> m_inDialogueTransactions = new Dictionary<string, string>(); // <Forwarded transaction id, Origin transaction id>.
  75. public static IPAddress PublicIPAddress;
  76. public SIPDialogueManager(
  77. SIPTransport sipTransport,
  78. SIPEndPoint outboundProxy,
  79. SIPMonitorLogDelegate logDelegate,
  80. SIPAssetPersistor<SIPDialogueAsset> sipDialoguePersistor,
  81. SIPAssetPersistor<SIPCDRAsset> sipCDRPersistor,
  82. SIPAuthenticateRequestDelegate authenticateRequestDelegate,
  83. SIPAssetGetDelegate<SIPAccount> getSIPAccount,
  84. GetCanonicalDomainDelegate getCanonicalDomain)
  85. {
  86. m_sipTransport = sipTransport;
  87. m_outboundProxy = outboundProxy;
  88. Log_External = logDelegate;
  89. m_sipDialoguePersistor = sipDialoguePersistor;
  90. m_sipCDRPersistor = sipCDRPersistor;
  91. SIPAuthenticateRequest_External = authenticateRequestDelegate;
  92. GetSIPAccount_External = getSIPAccount;
  93. GetCanonicalDomain_External = getCanonicalDomain;
  94. }
  95. public void ProcessInDialogueRequest(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPRequest sipRequest, SIPDialogue dialogue)
  96. {
  97. try
  98. {
  99. if (sipRequest.Method == SIPMethodsEnum.BYE)
  100. {
  101. SIPNonInviteTransaction byeTransaction = m_sipTransport.CreateNonInviteTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, m_outboundProxy);
  102. //logger.Debug("Matching dialogue found for BYE request to " + sipRequest.URI.ToString() + ".");
  103. SIPResponse byeResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null);
  104. byeTransaction.SendFinalResponse(byeResponse);
  105. string hangupReason = sipRequest.Header.Reason;
  106. if (hangupReason.IsNullOrBlank())
  107. {
  108. hangupReason = sipRequest.Header.GetUnknownHeaderValue("X-Asterisk-HangupCause");
  109. }
  110. if (sipRequest.Header.SwitchboardTerminate == "both")
  111. {
  112. // BYE request from switchboard that's requesting two dialogues to be hungup by the server.
  113. CallHungup(dialogue, hangupReason, true);
  114. }
  115. else
  116. {
  117. // Normal BYE request.
  118. CallHungup(dialogue, hangupReason, false);
  119. }
  120. }
  121. else if (sipRequest.Method == SIPMethodsEnum.INVITE)
  122. {
  123. UASInviteTransaction reInviteTransaction = m_sipTransport.CreateUASTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, m_outboundProxy);
  124. SIPResponse tryingResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Trying, null);
  125. reInviteTransaction.SendInformationalResponse(tryingResponse);
  126. reInviteTransaction.CDR = null; // Don't want CDR's on re-INVITEs.
  127. ForwardInDialogueRequest(dialogue, reInviteTransaction, localSIPEndPoint, remoteEndPoint);
  128. }
  129. else if (sipRequest.Method == SIPMethodsEnum.OPTIONS)
  130. {
  131. // Send back the remote SDP.
  132. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "OPTIONS request for established dialogue " + dialogue.DialogueName + ".", dialogue.Owner));
  133. SIPNonInviteTransaction optionsTransaction = m_sipTransport.CreateNonInviteTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, m_outboundProxy);
  134. SIPResponse okResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null);
  135. okResponse.Body = dialogue.RemoteSDP;
  136. okResponse.Header.ContentLength = okResponse.Body.Length;
  137. okResponse.Header.ContentType = m_sdpContentType;
  138. optionsTransaction.SendFinalResponse(okResponse);
  139. }
  140. else if (sipRequest.Method == SIPMethodsEnum.MESSAGE)
  141. {
  142. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "MESSAGE for call " + sipRequest.URI.ToString() + ": " + sipRequest.Body + ".", dialogue.Owner));
  143. SIPNonInviteTransaction messageTransaction = m_sipTransport.CreateNonInviteTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, m_outboundProxy);
  144. SIPResponse okResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null);
  145. messageTransaction.SendFinalResponse(okResponse);
  146. }
  147. else
  148. {
  149. // This is a request on an established call forward through to the opposite dialogue.
  150. SIPNonInviteTransaction passThruTransaction = m_sipTransport.CreateNonInviteTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, m_outboundProxy);
  151. ForwardInDialogueRequest(dialogue, passThruTransaction, localSIPEndPoint, remoteEndPoint);
  152. }
  153. }
  154. catch (Exception excp)
  155. {
  156. logger.Error("Exception ProcessInDialogueRequest. " + excp.Message);
  157. throw;
  158. }
  159. }
  160. public void ProcessInDialogueReferRequest(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPRequest sipRequest, SIPDialogue dialogue, Func<string, SIPURI, string, SIPDialogue, ISIPServerUserAgent> blindTransfer)
  161. {
  162. try
  163. {
  164. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "REFER received on dialogue " + dialogue.DialogueName + ", transfer mode is " + dialogue.TransferMode + ".", dialogue.Owner));
  165. SIPNonInviteTransaction referTransaction = m_sipTransport.CreateNonInviteTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, m_outboundProxy);
  166. if (sipRequest.Header.ReferTo.IsNullOrBlank())
  167. {
  168. // A REFER request must have a Refer-To header.
  169. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Bad REFER request, no Refer-To header.", dialogue.Owner));
  170. SIPResponse invalidResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.BadRequest, "Missing mandatory Refer-To header");
  171. referTransaction.SendFinalResponse(invalidResponse);
  172. }
  173. else
  174. {
  175. if (dialogue.TransferMode == SIPDialogueTransferModesEnum.NotAllowed)
  176. {
  177. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "REFER rejected due to dialogue permissions.", dialogue.Owner));
  178. SIPResponse declineTransferResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Decline, "Transfers are disabled on dialogue");
  179. referTransaction.SendFinalResponse(declineTransferResponse);
  180. }
  181. else if (Regex.Match(sipRequest.Header.ReferTo, m_referReplacesParameter).Success)
  182. {
  183. // Attended transfers are allowed unless explicitly blocked. Attended transfers are not dangerous
  184. // as no new call is created and it's the same as a re-invite.
  185. if (dialogue.TransferMode == SIPDialogueTransferModesEnum.PassThru)
  186. {
  187. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "REFER received, attended transfer, passing through, Referred-By=" + sipRequest.Header.ReferredBy + ".", dialogue.Owner));
  188. ForwardInDialogueRequest(dialogue, referTransaction, localSIPEndPoint, remoteEndPoint);
  189. }
  190. else
  191. {
  192. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "REFER received, attended transfer, processing on app server, Referred-By=" + sipRequest.Header.ReferredBy + ".", dialogue.Owner));
  193. ProcessAttendedRefer(dialogue, referTransaction, sipRequest, localSIPEndPoint, remoteEndPoint);
  194. }
  195. }
  196. else
  197. {
  198. bool referAuthenticated = false;
  199. if (dialogue.TransferMode == SIPDialogueTransferModesEnum.Default)
  200. {
  201. string canonicalDomain = GetCanonicalDomain_External(sipRequest.Header.From.FromURI.Host, false);
  202. if (!canonicalDomain.IsNullOrBlank())
  203. {
  204. referAuthenticated = AuthenticateReferRequest(referTransaction, sipRequest.Header.From.FromURI.User, canonicalDomain);
  205. }
  206. }
  207. if (dialogue.TransferMode == SIPDialogueTransferModesEnum.BlindPlaceCall || referAuthenticated)
  208. {
  209. // A blind transfer that is permitted to initiate a new call.
  210. //logger.Debug("Blind Transfer starting.");
  211. SIPResponse acceptedResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Accepted, null);
  212. referTransaction.SendFinalResponse(acceptedResponse);
  213. SendNotifyRequestForRefer(sipRequest, dialogue, localSIPEndPoint, SIPResponseStatusCodesEnum.Trying, null);
  214. //SIPDialogue oppositeDialogue = GetOppositeDialogue(dialogue);
  215. SIPUserField replacesUserField = SIPUserField.ParseSIPUserField(sipRequest.Header.ReferTo);
  216. ISIPServerUserAgent transferUAS = blindTransfer(dialogue.Owner, replacesUserField.URI, "transfer", dialogue);
  217. bool sendNotifications = true;
  218. Guid originalBridgeID = dialogue.BridgeId;
  219. transferUAS.UASStateChanged += (uas, status, reason) =>
  220. {
  221. if (sendNotifications)
  222. {
  223. if (status != SIPResponseStatusCodesEnum.Trying)
  224. {
  225. // As soon as a blind transfer receives a non-100 response break the bridge as most UA's will immediately hangup the call once
  226. // they are informed it's proceeding.
  227. if (dialogue.BridgeId != Guid.Empty)
  228. {
  229. dialogue.BridgeId = Guid.Empty;
  230. m_sipDialoguePersistor.UpdateProperty(dialogue.Id, "BridgeId", dialogue.BridgeId.ToString());
  231. }
  232. // Retrieve the dialogue anew each time a new response is received in order to check if it still exists.
  233. SIPDialogueAsset updatedDialogue = m_sipDialoguePersistor.Get(dialogue.Id);
  234. if (updatedDialogue != null)
  235. {
  236. SendNotifyRequestForRefer(sipRequest, updatedDialogue.SIPDialogue, localSIPEndPoint, status, reason);
  237. }
  238. else
  239. {
  240. // The dialogue the blind transfer notifications were being sent on has been hungup no pint sending any more notifications.
  241. sendNotifications = false;
  242. }
  243. }
  244. }
  245. if ((int)status >= 400)
  246. {
  247. // The transfer has failed. Attempt to re-bridge if possible or if not hangup the orphaned end of the call.
  248. SIPDialogueAsset referDialogue = m_sipDialoguePersistor.Get(dialogue.Id); // Dialogue that initiated the REFER.
  249. if (referDialogue != null)
  250. {
  251. 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));
  252. // Re-bridging the two original dialogues.
  253. m_sipDialoguePersistor.UpdateProperty(dialogue.Id, "BridgeId", originalBridgeID.ToString());
  254. }
  255. else
  256. {
  257. 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));
  258. // 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.
  259. string bridgeIDStr = originalBridgeID.ToString();
  260. SIPDialogueAsset orphanedDialogueAsset = m_sipDialoguePersistor.Get(d => d.BridgeId == bridgeIDStr);
  261. if (orphanedDialogueAsset != null)
  262. {
  263. HangupDialogue(orphanedDialogueAsset.SIPDialogue, "Blind transfer failed and remote end already hungup.", true);
  264. }
  265. }
  266. }
  267. };
  268. //logger.Debug("Blind Transfer successfully initated, dial plan processing now in progress.");
  269. }
  270. else
  271. {
  272. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "REFER received, blind transfer, Refer-To=" + sipRequest.Header.ReferTo + ", Referred-By=" + sipRequest.Header.ReferredBy + ".", dialogue.Owner));
  273. //SIPNonInviteTransaction passThruTransaction = m_sipTransport.CreateNonInviteTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, m_outboundProxy);
  274. ForwardInDialogueRequest(dialogue, referTransaction, localSIPEndPoint, remoteEndPoint);
  275. }
  276. }
  277. }
  278. }
  279. catch (Exception excp)
  280. {
  281. logger.Error("Exception ProcessInDialogueReferRequest. " + excp.Message);
  282. throw;
  283. }
  284. }
  285. private bool AuthenticateReferRequest(SIPNonInviteTransaction referTransaction, string sipUsername, string sipDomain)
  286. {
  287. SIPRequest referRequest = referTransaction.TransactionRequest;
  288. try
  289. {
  290. if (SIPAuthenticateRequest_External == null)
  291. {
  292. // No point trying to authenticate if we haven't been given an authentication delegate.
  293. logger.Warn("Missing SIP request authentication delegate in SIPDialogueManager AuthenticateReferRequest.");
  294. SIPResponse errorResponse = SIPTransport.GetResponse(referRequest, SIPResponseStatusCodesEnum.InternalServerError, null);
  295. referTransaction.SendFinalResponse(errorResponse);
  296. }
  297. else if (GetSIPAccount_External == null)
  298. {
  299. // No point trying to authenticate if we haven't been given a delegate to load the SIP account.
  300. logger.Warn("Missing get SIP account delegate in SIPDialogueManager AuthenticateReferRequest.");
  301. SIPResponse errorResponse = SIPTransport.GetResponse(referRequest, SIPResponseStatusCodesEnum.InternalServerError, null);
  302. referTransaction.SendFinalResponse(errorResponse);
  303. }
  304. else
  305. {
  306. SIPAccount sipAccount = GetSIPAccount_External(s => s.SIPUsername == sipUsername && s.SIPDomain == sipDomain);
  307. if (sipAccount == null)
  308. {
  309. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Rejecting authentication required call for " + sipUsername + "@" + sipDomain + ", SIP account not found.", null));
  310. SIPResponse errorResponse = SIPTransport.GetResponse(referRequest, SIPResponseStatusCodesEnum.Forbidden, null);
  311. referTransaction.SendFinalResponse(errorResponse);
  312. }
  313. else
  314. {
  315. SIPEndPoint localSIPEndPoint = (!referRequest.Header.ProxyReceivedOn.IsNullOrBlank()) ? SIPEndPoint.ParseSIPEndPoint(referRequest.Header.ProxyReceivedOn) : referRequest.LocalSIPEndPoint;
  316. SIPEndPoint remoteEndPoint = (!referRequest.Header.ProxyReceivedFrom.IsNullOrBlank()) ? SIPEndPoint.ParseSIPEndPoint(referRequest.Header.ProxyReceivedFrom) : referRequest.RemoteSIPEndPoint;
  317. SIPRequestAuthenticationResult authenticationResult = SIPAuthenticateRequest_External(localSIPEndPoint, remoteEndPoint, referRequest, sipAccount, Log_External);
  318. if (authenticationResult.Authenticated)
  319. {
  320. if (authenticationResult.WasAuthenticatedByIP)
  321. {
  322. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "REFER from " + remoteEndPoint.ToString() + " successfully authenticated by IP address.", sipAccount.Owner));
  323. }
  324. else
  325. {
  326. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "REFER from " + remoteEndPoint.ToString() + " successfully authenticated by digest.", sipAccount.Owner));
  327. }
  328. return true;
  329. }
  330. else
  331. {
  332. // Send authorisation failure or required response
  333. SIPResponse authReqdResponse = SIPTransport.GetResponse(referRequest, authenticationResult.ErrorResponse, null);
  334. authReqdResponse.Header.AuthenticationHeader = authenticationResult.AuthenticationRequiredHeader;
  335. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "REFER not authenticated for " + sipUsername + "@" + sipDomain + ", responding with " + authenticationResult.ErrorResponse + ".", null));
  336. referTransaction.SendFinalResponse(authReqdResponse);
  337. return false;
  338. }
  339. }
  340. }
  341. return false;
  342. }
  343. catch (Exception excp)
  344. {
  345. logger.Error("Exception SIDialogueManager AuthenticateReferRequest. " + excp.Message);
  346. SIPResponse errorResponse = SIPTransport.GetResponse(referRequest, SIPResponseStatusCodesEnum.InternalServerError, null);
  347. referTransaction.SendFinalResponse(errorResponse);
  348. return false;
  349. }
  350. }
  351. public void CreateDialogueBridge(SIPDialogue clientDiaglogue, SIPDialogue forwardedDialogue, string owner)
  352. {
  353. logger.Debug("Creating dialogue bridge between " + clientDiaglogue.DialogueName + " and " + forwardedDialogue.DialogueName + ".");
  354. Guid bridgeId = Guid.NewGuid();
  355. clientDiaglogue.BridgeId = bridgeId;
  356. forwardedDialogue.BridgeId = bridgeId;
  357. m_sipDialoguePersistor.Add(new SIPDialogueAsset(clientDiaglogue));
  358. m_sipDialoguePersistor.Add(new SIPDialogueAsset(forwardedDialogue));
  359. SIPEndPoint clientDialogueRemoteEP = (IPSocket.IsIPSocket(clientDiaglogue.RemoteTarget.Host)) ? SIPEndPoint.ParseSIPEndPoint(clientDiaglogue.RemoteTarget.Host) : null;
  360. Log_External(new SIPMonitorMachineEvent(SIPMonitorMachineEventTypesEnum.SIPDialogueCreated, clientDiaglogue.Owner, clientDiaglogue.Id.ToString(), clientDiaglogue.LocalUserField.URI));
  361. SIPEndPoint forwardedDialogueRemoteEP = (IPSocket.IsIPSocket(forwardedDialogue.RemoteTarget.Host)) ? SIPEndPoint.ParseSIPEndPoint(forwardedDialogue.RemoteTarget.Host) : null;
  362. Log_External(new SIPMonitorMachineEvent(SIPMonitorMachineEventTypesEnum.SIPDialogueCreated, forwardedDialogue.Owner, forwardedDialogue.Id.ToString(), forwardedDialogue.LocalUserField.URI));
  363. }
  364. /// <summary>
  365. /// This method takes the necessary actions to terminate a bridged call.
  366. /// </summary>
  367. /// <param name="sipDialogue">The dialogue that the BYE request was received on.</param>
  368. /// <param name="hangupCause">If present an informational field to indicate the hangup cause.</param>
  369. /// <param name="sendBYEForOriginDialogue">If true means a BYE should be sent for the origin dialogue as well. This is used when a 3rd party
  370. /// call control agent is attempting to hangup a call.</param>
  371. public void CallHungup(SIPDialogue sipDialogue, string hangupCause, bool sendBYEForOriginDialogue)
  372. {
  373. try
  374. {
  375. if (sipDialogue != null)
  376. {
  377. //logger.Debug("BYE received on dialogue " + sipDialogue.DialogueName + ".");
  378. HangupDialogue(sipDialogue, hangupCause, sendBYEForOriginDialogue);
  379. if (sipDialogue.BridgeId != Guid.Empty)
  380. {
  381. SIPDialogue orphanedDialogue = GetOppositeDialogue(sipDialogue);
  382. if (orphanedDialogue != null)
  383. {
  384. HangupDialogue(orphanedDialogue, m_remoteHangupCause, true);
  385. }
  386. }
  387. else
  388. {
  389. logger.Warn("No bridge could be found for hungup call.");
  390. }
  391. }
  392. }
  393. catch (Exception excp)
  394. {
  395. logger.Error("Exception CallManager CallHungup. " + excp.Message);
  396. }
  397. }
  398. private void HangupDialogue(SIPDialogue dialogue, string hangupCause, bool sendBye)
  399. {
  400. try
  401. {
  402. //logger.Debug("Hanging up orphaned dialogue " + dialogue.DialogueName + ".");
  403. if (dialogue.CDRId != Guid.Empty)
  404. {
  405. SIPCDRAsset cdr = m_sipCDRPersistor.Get(dialogue.CDRId);
  406. if (cdr != null)
  407. {
  408. cdr.BridgeId = dialogue.BridgeId.ToString();
  409. cdr.Hungup(hangupCause);
  410. }
  411. else
  412. {
  413. logger.Warn("CDR could not be found for remote dialogue in SIPCallManager CallHungup.");
  414. }
  415. }
  416. else
  417. {
  418. logger.Warn("There was no CDR attached to orphaned dialogue in SIPCallManager CallHungup.");
  419. }
  420. if (sendBye)
  421. {
  422. dialogue.Hangup(m_sipTransport, m_outboundProxy);
  423. }
  424. m_sipDialoguePersistor.Delete(new SIPDialogueAsset(dialogue));
  425. SIPEndPoint orphanedDialogueRemoteEP = (IPSocket.IsIPSocket(dialogue.RemoteTarget.Host)) ? SIPEndPoint.ParseSIPEndPoint(dialogue.RemoteTarget.Host) : null;
  426. Log_External(new SIPMonitorMachineEvent(SIPMonitorMachineEventTypesEnum.SIPDialogueRemoved, dialogue.Owner, dialogue.Id.ToString(), dialogue.LocalUserField.URI));
  427. }
  428. catch (Exception excp)
  429. {
  430. logger.Error("Exception HangupDialogue. " + excp.Message);
  431. }
  432. }
  433. /// <summary>
  434. /// Attempts to locate a dialogue for an in-dialogue transaction.
  435. /// </summary>
  436. /// <param name="transaction"></param>
  437. /// <returns></returns>
  438. public SIPDialogue GetDialogue(SIPRequest sipRequest)
  439. {
  440. try
  441. {
  442. string callId = sipRequest.Header.CallId;
  443. string localTag = sipRequest.Header.To.ToTag;
  444. string remoteTag = sipRequest.Header.From.FromTag;
  445. return GetDialogue(callId, localTag, remoteTag);
  446. }
  447. catch (Exception excp)
  448. {
  449. logger.Error("Exception GetDialogue. " + excp);
  450. return null;
  451. }
  452. }
  453. public SIPDialogue GetDialogue(string replaces)
  454. {
  455. try
  456. {
  457. if (replaces.IsNullOrBlank())
  458. {
  459. return null;
  460. }
  461. string unescapedReplaces = SIPEscape.SIPUnescapeString(replaces);
  462. Match replacesMatch = Regex.Match(unescapedReplaces, "^(?<callid>.*?);to-tag=(?<totag>.*?);from-tag=(?<fromtag>.*)");
  463. if (replacesMatch.Success)
  464. {
  465. string callId = replacesMatch.Result("${callid}");
  466. string localTag = replacesMatch.Result("${totag}");
  467. string remoteTag = replacesMatch.Result("${fromtag}");
  468. logger.Debug("Call-ID=" + callId + ", localtag=" + localTag + ", remotetag=" + remoteTag + ".");
  469. SIPDialogue replacesDialogue = GetDialogue(callId, localTag, remoteTag);
  470. if (replacesDialogue == null)
  471. {
  472. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "A dialogue was not found for the Replaces parameter on a Refer-To header.", null));
  473. }
  474. return replacesDialogue;
  475. }
  476. else
  477. {
  478. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "The Replaces parameter on a Refer-To header was not in the expected fromat, " + replaces + ".", null));
  479. return null;
  480. }
  481. }
  482. catch (Exception excp)
  483. {
  484. logger.Error("Exception GetDialogue (replaces). " + excp);
  485. return null;
  486. }
  487. }
  488. public SIPDialogue GetDialogue(string callId, string localTag, string remoteTag)
  489. {
  490. try
  491. {
  492. //string dialogueId = SIPDialogue.GetDialogueId(callId, localTag, remoteTag);
  493. SIPDialogueAsset dialogueAsset = m_sipDialoguePersistor.Get(d => d.CallId == callId && d.LocalTag == localTag && d.RemoteTag == remoteTag);
  494. if (dialogueAsset != null)
  495. {
  496. //logger.Debug("SIPDialogueManager dialogue match correctly found on dialogue hash.");
  497. return dialogueAsset.SIPDialogue;
  498. }
  499. else
  500. {
  501. // Try on To tag.
  502. dialogueAsset = m_sipDialoguePersistor.Get(d => d.LocalTag == localTag);
  503. if (dialogueAsset != null)
  504. {
  505. logger.Warn("SIPDialogueManager dialogue match found on fallback mechanism of To tag.");
  506. return dialogueAsset.SIPDialogue;
  507. }
  508. // Try on From tag.
  509. dialogueAsset = m_sipDialoguePersistor.Get(d => d.RemoteTag == remoteTag);
  510. if (dialogueAsset != null)
  511. {
  512. logger.Warn("SIPDialogueManager dialogue match found on fallback mechanism of From tag.");
  513. return dialogueAsset.SIPDialogue;
  514. }
  515. // As an experiment will try on the Call-ID as well. However as a safeguard it will only succeed if there is only one instance of the
  516. // Call-ID in use. Since the Call-ID is not mandated by the SIP standard as being unique there it may be that matching on it causes more
  517. // problems then it solves.
  518. dialogueAsset = m_sipDialoguePersistor.Get(d => d.CallId == callId);
  519. if (dialogueAsset != null)
  520. {
  521. logger.Warn("SIPDialogueManager dialogue match found on fallback mechanism of Call-ID.");
  522. return dialogueAsset.SIPDialogue;
  523. }
  524. }
  525. return null;
  526. }
  527. catch (Exception excp)
  528. {
  529. logger.Error("Exception GetDialogue. " + excp);
  530. return null;
  531. }
  532. }
  533. /// <summary>
  534. /// This method applies very liberal rules to find a matching dialogue:
  535. /// 1. Treat the call identifier as a Call-ID,
  536. /// 2. If no dialogue matches for that try with the call identifier as the from username on the local user field,
  537. /// </summary>
  538. /// <param name="owner">The dialogue owner to use when attempting to find a match.</param>
  539. /// <param name="callIdentifier">A call identifier field to try and match a dialogue against.</param>
  540. /// <returns>A dialogue if a match is found or null otherwise.</returns>
  541. public SIPDialogue GetDialogueRelaxed(string owner, string callIdentifier)
  542. {
  543. if (owner.IsNullOrBlank() || callIdentifier.IsNullOrBlank())
  544. {
  545. return null;
  546. }
  547. else
  548. {
  549. owner = owner.ToLower();
  550. SIPDialogue callIDDialogue = GetDialogue(callIdentifier, null, null);
  551. if (callIDDialogue != null && callIDDialogue.Owner == owner)
  552. {
  553. return callIDDialogue;
  554. }
  555. else
  556. {
  557. List<SIPDialogueAsset> dialogueAssets = m_sipDialoguePersistor.Get(d => d.Owner == owner, null, 0, Int32.MaxValue);
  558. if (dialogueAssets != null && dialogueAssets.Count > 0)
  559. {
  560. SIPDialogueAsset matchingDialogue = null;
  561. foreach (SIPDialogueAsset dialogueAsset in dialogueAssets)
  562. {
  563. //if (dialogueAsset.LocalUserField.Contains(callIdentifier))
  564. if (dialogueAsset.RemoteUserField.Contains(callIdentifier))
  565. {
  566. if (matchingDialogue == null)
  567. {
  568. matchingDialogue = dialogueAsset;
  569. }
  570. else
  571. {
  572. // Ambiguous match, two or more dialogues match when matching on the call identifier string.
  573. return null;
  574. }
  575. }
  576. }
  577. if (matchingDialogue != null)
  578. {
  579. return matchingDialogue.SIPDialogue;
  580. }
  581. }
  582. }
  583. }
  584. return null;
  585. }
  586. /// <summary>
  587. /// Retrieves the other end of a call given the dialogue from one end.
  588. /// </summary>
  589. /// <param name="dialogue"></param>
  590. /// <returns></returns>
  591. public SIPDialogue GetOppositeDialogue(SIPDialogue dialogue)
  592. {
  593. if (dialogue.BridgeId != Guid.Empty)
  594. {
  595. string bridgeIdString = dialogue.BridgeId.ToString();
  596. SIPDialogueAsset dialogueAsset = m_sipDialoguePersistor.Get(d => d.BridgeId == bridgeIdString && d.Id != dialogue.Id);
  597. return (dialogueAsset != null) ? dialogueAsset.SIPDialogue : null;
  598. }
  599. else
  600. {
  601. return null;
  602. }
  603. }
  604. /// <summary>
  605. /// Attempts to reinvite an existing end of a call by sending a new SDP.
  606. /// </summary>
  607. /// <param name="dialogue">The dialogue describing the end of the call to be re-invited.</param>
  608. /// <param name="newSDP">The session description for the new dialogue desired.</param>
  609. public void ReInvite(SIPDialogue dialogue, SIPDialogue substituteDialogue)
  610. {
  611. try
  612. {
  613. string replacementSDP = substituteDialogue.RemoteSDP;
  614. // Determine whether the SDP needs to be mangled.
  615. IPEndPoint dialogueSDPSocket = SDP.GetSDPRTPEndPoint(dialogue.RemoteSDP);
  616. IPEndPoint replacementSDPSocket = SDP.GetSDPRTPEndPoint(substituteDialogue.RemoteSDP);
  617. bool wasMangled = false;
  618. if (!IPSocket.IsPrivateAddress(dialogueSDPSocket.Address.ToString()) && IPSocket.IsPrivateAddress(replacementSDPSocket.Address.ToString()))
  619. {
  620. // 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.
  621. string publicIPAddress = (PublicIPAddress != null) ? PublicIPAddress.ToString() : IPSocket.ParseHostFromSocket(substituteDialogue.RemoteTarget.Host);
  622. replacementSDP = SIPPacketMangler.MangleSDP(replacementSDP, publicIPAddress, out wasMangled);
  623. }
  624. if (wasMangled)
  625. {
  626. logger.Debug("The SDP being used in a re-INVITE was mangled to " + SDP.GetSDPRTPEndPoint(replacementSDP) + ".");
  627. }
  628. // Check whether there is a need to send the re-invite by comparing the new SDP being sent with what has already been sent.
  629. if (dialogue.SDP == replacementSDP)
  630. {
  631. logger.Debug("A reinvite was not sent to " + dialogue.RemoteTarget.ToString() + " as the SDP has not changed.");
  632. }
  633. else
  634. {
  635. logger.Debug("Reinvite SDP being sent to " + dialogue.RemoteTarget.ToString() + ":\r\n" + replacementSDP);
  636. dialogue.CSeq = dialogue.CSeq + 1;
  637. m_sipDialoguePersistor.UpdateProperty(dialogue.Id, "CSeq", dialogue.CSeq);
  638. SIPEndPoint localSIPEndPoint = (m_outboundProxy != null) ? m_sipTransport.GetDefaultTransportContact(m_outboundProxy.Protocol) : m_sipTransport.GetDefaultTransportContact(SIPProtocolsEnum.udp);
  639. SIPRequest reInviteReq = GetInviteRequest(dialogue, localSIPEndPoint, replacementSDP);
  640. SIPEndPoint reinviteEndPoint = null;
  641. // If the outbound proxy is a loopback address, as it will normally be for local deployments, then it cannot be overriden.
  642. if (m_outboundProxy != null && IPAddress.IsLoopback(m_outboundProxy.Address))
  643. {
  644. reInviteReq.Header.ProxySendFrom = dialogue.ProxySendFrom;
  645. reinviteEndPoint = m_outboundProxy;
  646. }
  647. if (!dialogue.ProxySendFrom.IsNullOrBlank())
  648. {
  649. reInviteReq.Header.ProxySendFrom = dialogue.ProxySendFrom;
  650. // The proxy will always be listening on UDP port 5060 for requests from internal servers.
  651. reinviteEndPoint = new SIPEndPoint(SIPProtocolsEnum.udp, new IPEndPoint(SIPEndPoint.ParseSIPEndPoint(dialogue.ProxySendFrom).Address, m_defaultSIPPort));
  652. }
  653. else
  654. {
  655. SIPDNSLookupResult lookupResult = m_sipTransport.GetRequestEndPoint(reInviteReq, m_outboundProxy, false);
  656. if (lookupResult.LookupError != null)
  657. {
  658. logger.Warn("ReInvite Failed to resolve " + lookupResult.URI.Host + ".");
  659. }
  660. else
  661. {
  662. reinviteEndPoint = lookupResult.GetSIPEndPoint();
  663. }
  664. }
  665. if (reinviteEndPoint != null)
  666. {
  667. UACInviteTransaction reInviteTransaction = m_sipTransport.CreateUACTransaction(reInviteReq, reinviteEndPoint, localSIPEndPoint, reinviteEndPoint);
  668. reInviteTransaction.CDR = null; // Don't want CDRs on re-invites.
  669. reInviteTransaction.UACInviteTransactionFinalResponseReceived += ReInviteTransactionFinalResponseReceived;
  670. reInviteTransaction.SendInviteRequest(reinviteEndPoint, reInviteReq);
  671. }
  672. else
  673. {
  674. throw new ApplicationException("Could not forward re-invite as request end point could not be determined.\r\n" + reInviteReq.ToString());
  675. }
  676. }
  677. }
  678. catch (Exception excp)
  679. {
  680. logger.Error("Exception CallManager ReInvite. " + excp.Message);
  681. throw excp;
  682. }
  683. }
  684. private void ReInviteTransactionFinalResponseReceived(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPTransaction sipTransaction, SIPResponse sipResponse)
  685. {
  686. try
  687. {
  688. //SIPRequest inviteRequest = sipTransaction.TransactionRequest;
  689. //SIPDialogue dialogue = GetDialogue(inviteRequest.Header.CallId, inviteRequest.Header.From.FromTag, inviteRequest.Header.To.ToTag);
  690. //m_dialogueBridges[dialogueId] = m_reInvitedDialogues[dialogueId];
  691. //m_reInvitedDialogues.Remove(dialogueId);
  692. if (sipResponse.StatusCode >= 200 && sipResponse.StatusCode <= 299)
  693. {
  694. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Reinvite request " + sipTransaction.TransactionRequest.URI.ToString() + " succeeeded with " + sipResponse.Status + ".", null));
  695. }
  696. else
  697. {
  698. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Reinvite request " + sipTransaction.TransactionRequest.URI.ToString() + " failed with " + sipResponse.Status + " " + sipResponse.ReasonPhrase + ".", null));
  699. }
  700. }
  701. catch (Exception excp)
  702. {
  703. logger.Error("Exception ReInviteTransactionFinalResponseReceived. " + excp.Message);
  704. throw excp;
  705. }
  706. }
  707. private void ForwardInDialogueRequest(SIPDialogue dialogue, SIPTransaction inDialogueTransaction, SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint)
  708. {
  709. try
  710. {
  711. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "In dialogue request " + inDialogueTransaction.TransactionRequest.Method + " received from for uri=" + inDialogueTransaction.TransactionRequest.URI.ToString() + ".", null));
  712. // Update the CSeq based on the latest received request.
  713. dialogue.CSeq = inDialogueTransaction.TransactionRequest.Header.CSeq;
  714. // Get the dialogue for the other end of the bridge.
  715. SIPDialogue bridgedDialogue = GetOppositeDialogue(dialogue);
  716. SIPEndPoint forwardSIPEndPoint = m_sipTransport.GetDefaultSIPEndPoint(new SIPEndPoint(bridgedDialogue.RemoteTarget));
  717. IPAddress remoteUAIPAddress = (inDialogueTransaction.TransactionRequest.Header.ProxyReceivedFrom.IsNullOrBlank()) ? remoteEndPoint.Address : SIPEndPoint.ParseSIPEndPoint(inDialogueTransaction.TransactionRequest.Header.ProxyReceivedFrom).Address;
  718. SIPRequest forwardedRequest = inDialogueTransaction.TransactionRequest.Copy();
  719. forwardedRequest.URI = bridgedDialogue.RemoteTarget;
  720. forwardedRequest.Header.Routes = bridgedDialogue.RouteSet;
  721. forwardedRequest.Header.CallId = bridgedDialogue.CallId;
  722. bridgedDialogue.CSeq = bridgedDialogue.CSeq + 1;
  723. forwardedRequest.Header.CSeq = bridgedDialogue.CSeq;
  724. forwardedRequest.Header.To = new SIPToHeader(bridgedDialogue.RemoteUserField.Name, bridgedDialogue.RemoteUserField.URI, bridgedDialogue.RemoteTag);
  725. forwardedRequest.Header.From = new SIPFromHeader(bridgedDialogue.LocalUserField.Name, bridgedDialogue.LocalUserField.URI, bridgedDialogue.LocalTag);
  726. forwardedRequest.Header.Contact = new List<SIPContactHeader>() { new SIPContactHeader(null, new SIPURI(bridgedDialogue.RemoteTarget.Scheme, forwardSIPEndPoint)) };
  727. forwardedRequest.Header.Vias = new SIPViaSet();
  728. forwardedRequest.Header.Vias.PushViaHeader(new SIPViaHeader(forwardSIPEndPoint, CallProperties.CreateBranchId()));
  729. forwardedRequest.Header.UserAgent = m_userAgentString;
  730. forwardedRequest.Header.AuthenticationHeader = null;
  731. if (inDialogueTransaction.TransactionRequest.Body != null && inDialogueTransaction.TransactionRequest.Method == SIPMethodsEnum.INVITE)
  732. {
  733. bool wasMangled = false;
  734. forwardedRequest.Body = SIPPacketMangler.MangleSDP(inDialogueTransaction.TransactionRequest.Body, remoteUAIPAddress.ToString(), out wasMangled);
  735. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Re-INVITE wasmangled=" + wasMangled + " remote=" + remoteUAIPAddress.ToString() + ".", null));
  736. forwardedRequest.Header.ContentLength = forwardedRequest.Body.Length;
  737. }
  738. SIPEndPoint forwardEndPoint = null;
  739. if (!bridgedDialogue.ProxySendFrom.IsNullOrBlank())
  740. {
  741. forwardedRequest.Header.ProxySendFrom = bridgedDialogue.ProxySendFrom;
  742. // The proxy will always be listening on UDP port 5060 for requests from internal servers.
  743. forwardEndPoint = new SIPEndPoint(SIPProtocolsEnum.udp, new IPEndPoint(SIPEndPoint.ParseSIPEndPoint(bridgedDialogue.ProxySendFrom).Address, m_defaultSIPPort));
  744. }
  745. else
  746. {
  747. SIPDNSLookupResult lookupResult = m_sipTransport.GetRequestEndPoint(forwardedRequest, m_outboundProxy, false);
  748. if (lookupResult.LookupError != null)
  749. {
  750. logger.Warn("ForwardInDialogueRequest Failed to resolve " + lookupResult.URI.Host + ".");
  751. }
  752. else
  753. {
  754. forwardEndPoint = lookupResult.GetSIPEndPoint();
  755. }
  756. }
  757. if (forwardEndPoint != null)
  758. {
  759. if (inDialogueTransaction.TransactionRequest.Method == SIPMethodsEnum.INVITE)
  760. {
  761. UACInviteTransaction forwardedTransaction = m_sipTransport.CreateUACTransaction(forwardedRequest, forwardEndPoint, localSIPEndPoint, m_outboundProxy);
  762. forwardedTransaction.CDR = null; // Don't want CDR's on re-INVITES.
  763. forwardedTransaction.UACInviteTransactionFinalResponseReceived += InDialogueTransactionFinalResponseReceived;
  764. forwardedTransaction.UACInviteTransactionInformationResponseReceived += InDialogueTransactionInfoResponseReceived;
  765. forwardedTransaction.TransactionRemoved += InDialogueTransactionRemoved;
  766. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Forwarding re-INVITE from " + remoteEndPoint + " to " + forwardedRequest.URI.ToString() + ", first hop " + forwardEndPoint + ".", dialogue.Owner));
  767. forwardedTransaction.SendReliableRequest();
  768. lock (m_inDialogueTransactions)
  769. {
  770. m_inDialogueTransactions.Add(forwardedTransaction.TransactionId, inDialogueTransaction.TransactionId);
  771. }
  772. }
  773. else
  774. {
  775. SIPNonInviteTransaction forwardedTransaction = m_sipTransport.CreateNonInviteTransaction(forwardedRequest, forwardEndPoint, localSIPEndPoint, m_outboundProxy);
  776. forwardedTransaction.NonInviteTransactionFinalResponseReceived += InDialogueTransactionFinalResponseReceived;
  777. forwardedTransaction.NonInviteTransactionInfoResponseReceived += InDialogueTransactionInfoResponseReceived;
  778. forwardedTransaction.TransactionRemoved += InDialogueTransactionRemoved;
  779. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Forwarding in dialogue " + forwardedRequest.Method + " from " + remoteEndPoint + " to " + forwardedRequest.URI.ToString() + ", first hop " + forwardEndPoint + ".", dialogue.Owner));
  780. forwardedTransaction.SendReliableRequest();
  781. lock (m_inDialogueTransactions)
  782. {
  783. m_inDialogueTransactions.Add(forwardedTransaction.TransactionId, inDialogueTransaction.TransactionId);
  784. }
  785. }
  786. // Update the dialogues CSeqs so future in dialogue requests can be forwarded correctly.
  787. m_sipDialoguePersistor.UpdateProperty(bridgedDialogue.Id, "CSeq", bridgedDialogue.CSeq);
  788. m_sipDialoguePersistor.UpdateProperty(dialogue.Id, "CSeq", dialogue.CSeq);
  789. }
  790. else
  791. {
  792. 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));
  793. }
  794. }
  795. catch (Exception excp)
  796. {
  797. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.Error, "Exception forwarding in dialogue request. " + excp.Message, dialogue.Owner));
  798. }
  799. }
  800. /// <summary>
  801. /// Performs an attended transfer based on a REFER request with a Replaces parameter on the Refer-To header.
  802. /// </summary>
  803. /// <param name="dialogue">The dialogue matching the the REFER request headers (Call-ID, To tag and From tag).</param>
  804. /// <param name="referTransaction">The REFER request.</param>
  805. /// <param name="localEndPoint">The local SIP end point the REFER request was received on.</param>
  806. /// <param name="remoteEndPoint">The remote SIP end point the REFER request was received from.</param>
  807. private void ProcessAttendedRefer(SIPDialogue dialogue, SIPNonInviteTransaction referTransaction, SIPRequest referRequest, SIPEndPoint localEndPoint, SIPEndPoint remoteEndPoint)
  808. {
  809. try
  810. {
  811. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Initiating attended transfer .", dialogue.Owner));
  812. SIPUserField referToField = SIPUserField.ParseSIPUserField(referRequest.Header.ReferTo);
  813. if (referToField == null)
  814. {
  815. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Error on transfer, could not parse Refer-To header: " + referRequest.Header.ReferTo + ".", dialogue.Owner));
  816. SIPResponse errorResponse = SIPTransport.GetResponse(referRequest, SIPResponseStatusCodesEnum.BadRequest, "Could not parse Refer-To header");
  817. referTransaction.SendFinalResponse(errorResponse);
  818. }
  819. else
  820. {
  821. string replaces = referToField.URI.Headers.Get(m_referReplacesParameter);
  822. SIPDialogue replacesDialogue = GetDialogue(replaces);
  823. if (replacesDialogue == null)
  824. {
  825. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Could not locate the dialogue for the Replaces parameter on an attended transfer.", dialogue.Owner));
  826. SIPResponse errorResponse = SIPTransport.GetResponse(referRequest, SIPResponseStatusCodesEnum.BadRequest, "Could not locate replaced dialogue");
  827. referTransaction.SendFinalResponse(errorResponse);
  828. }
  829. else
  830. {
  831. logger.Debug("REFER dialogue being replaced " + replacesDialogue.DialogueName + ".");
  832. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Replacement dialogue found on Refer, accepting.", dialogue.Owner));
  833. bool sendNotifications = true;
  834. if(!referRequest.Header.ReferSub.IsNullOrBlank())
  835. {
  836. Boolean.TryParse(referRequest.Header.ReferSub, out sendNotifications);
  837. }
  838. SIPDialogue remainingDialogue = GetOppositeDialogue(replacesDialogue);
  839. SIPDialogue remaining2Dialogue = GetOppositeDialogue(dialogue);
  840. logger.Debug("REFER dialogue remaining " + remainingDialogue.DialogueName + ".");
  841. Guid newBridgeId = Guid.NewGuid();
  842. remainingDialogue.BridgeId = newBridgeId;
  843. remainingDialogue.CSeq++;
  844. remaining2Dialogue.BridgeId = newBridgeId;
  845. remaining2Dialogue.CSeq++;
  846. m_sipDialoguePersistor.Update(new SIPDialogueAsset(remainingDialogue));
  847. m_sipDialoguePersistor.Update(new SIPDialogueAsset(remaining2Dialogue));
  848. Log_External(new SIPMonitorMachineEvent(SIPMonitorMachineEventTypesEnum.SIPDialogueUpdated, remainingDialogue.Owner, remainingDialogue.Id.ToString(), remainingDialogue.LocalUserField.URI));
  849. Log_External(new SIPMonitorMachineEvent(SIPMonitorMachineEventTypesEnum.SIPDialogueUpdated, remaining2Dialogue.Owner, remaining2Dialogue.Id.ToString(), remaining2Dialogue.LocalUserField.URI));
  850. Log_External(new SIPMonitorMachineEvent(SIPMonitorMachineEventTypesEnum.SIPDialogueTransfer, remainingDialogue.Owner, remainingDialogue.Id.ToString(), remainingDialogue.LocalUserField.URI));
  851. SIPResponse acceptedResponse = SIPTransport.GetResponse(referRequest, SIPResponseStatusCodesEnum.Accepted, null);
  852. referTransaction.SendFinalResponse(acceptedResponse);
  853. if (sendNotifications)
  854. {
  855. SendNotifyRequestForRefer(referRequest, dialogue, localEndPoint, SIPResponseStatusCodesEnum.Trying, null);
  856. }
  857. logger.Debug("Reinviting " + remainingDialogue.DialogueName + " with " + remaining2Dialogue.DialogueName + ".");
  858. ReInvite(remainingDialogue, remaining2Dialogue);
  859. ReInvite(remaining2Dialogue, remainingDialogue);
  860. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Transfer dialogue re-invites complete.", dialogue.Owner));
  861. if (sendNotifications)
  862. {
  863. SendNotifyRequestForRefer(referRequest, dialogue, localEndPoint, SIPResponseStatusCodesEnum.Ok, null);
  864. }
  865. // Hangup redundant dialogues.
  866. logger.Debug("Hanging up redundant dialogues post transfer.");
  867. logger.Debug("Hanging up " + dialogue.DialogueName + ".");
  868. dialogue.Hangup(m_sipTransport, m_outboundProxy);
  869. CallHungup(dialogue, "Attended transfer", false);
  870. logger.Debug("Hanging up " + replacesDialogue.DialogueName + ".");
  871. replacesDialogue.Hangup(m_sipTransport, m_outboundProxy);
  872. CallHungup(replacesDialogue, "Attended transfer", false);
  873. }
  874. }
  875. }
  876. catch (Exception excp)
  877. {
  878. logger.Error("Exception ProcessAttendedRefer. " + excp.Message);
  879. throw;
  880. }
  881. }
  882. /// <summary>
  883. /// Performs transfer between 3 established dialogues (answered calls). The dead dialogue is being replaced by
  884. /// the answered dialogue such that a bridged call between the dead and orphaned dialogues now becomes one between the
  885. /// orphaned and answered dialogues.
  886. /// </summary>
  887. /// <param name="deadDialogue">The dialogue that will be terminated.</param>
  888. /// <param name="orphanedDialogue">The opposite side of the dead dialogue that will be bridged with the answered dialogue.</param>
  889. /// <param name="answeredDialogue">The newly answered dialogue that will be bridged with the orpahned dialogue.</param>
  890. public void DialogueTransfer(SIPDialogue deadDialogue, SIPDialogue orphanedDialogue, SIPDialogue answeredDialogue)
  891. {
  892. try
  893. {
  894. //logger.Debug("SIPDialogueManager DialogueTransfer.");
  895. // Create bridge between answered dialogue and other end of dialogue being replaced.
  896. Guid newBridgeId = Guid.NewGuid();
  897. orphanedDialogue.BridgeId = newBridgeId;
  898. answeredDialogue.BridgeId = newBridgeId;
  899. m_sipDialoguePersistor.Update(new SIPDialogueAsset(orphanedDialogue));
  900. m_sipDialoguePersistor.Add(new SIPDialogueAsset(answeredDialogue));
  901. Log_External(new SIPMonitorMachineEvent(SIPMonitorMachineEventTypesEnum.SIPDialogueCreated, answeredDialogue.Owner, answeredDialogue.Id.ToString(), answeredDialogue.LocalUserField.URI));
  902. Log_External(new SIPMonitorMachineEvent(SIPMonitorMachineEventTypesEnum.SIPDialogueUpdated, orphanedDialogue.Owner, orphanedDialogue.Id.ToString(), orphanedDialogue.LocalUserField.URI));
  903. //logger.Debug("Hanging up dead dialogue");
  904. // Hangup dialogue being replaced.
  905. // Check if the dead dialogue has already been hungup. For blind transfers the remote end will usually hangup once it gets a NOTIFY request
  906. // indicating the transfer is in progress.
  907. SIPDialogueAsset deadDialogueAsset = m_sipDialoguePersistor.Get(deadDialogue.Id);
  908. if (deadDialogueAsset != null)
  909. {
  910. deadDialogueAsset.SIPDialogue.Hangup(m_sipTransport, m_outboundProxy);
  911. CallHungup(deadDialogue, "Blind transfer", false);
  912. }
  913. //logger.Debug("Reinviting two remaining dialogues");
  914. // Reinvite other end of dialogue being replaced to answered dialogue.
  915. ReInvite(orphanedDialogue, answeredDialogue);
  916. //ReInvite(answeredDialogue, orphanedDialogue.SDP);
  917. }
  918. catch (Exception excp)
  919. {
  920. logger.Error("Exception DialogueTransfer. " + excp.Message);
  921. }
  922. }
  923. /// <summary>
  924. /// An attended transfer between two separate established calls where one leg of each call is being transferred
  925. /// to the other.
  926. /// </summary>
  927. /// <param name="callID1">The Call-ID of the first call leg that is no longer required and of which the opposite end will be transferred.</param>
  928. /// <param name="callID2">The Call-ID of the second call leg that is no longer required and of which the opposite end will be transferred.</param>
  929. public void DualTransfer(string username, string callID1, string callID2)
  930. {
  931. try
  932. {
  933. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "A dual transfer request was received for Call-ID's " + callID1 + " and " + callID2 + ".", username));
  934. if (username.IsNullOrBlank())
  935. {
  936. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Dual transfer was called with an empty username, the transfer was not initiated.", username));
  937. throw new ApplicationException("Dual transfer was called with an empty username, the transfer was not initiated.");
  938. }
  939. else if (callID1.IsNullOrBlank())
  940. {
  941. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Dual transfer was called with an empty value for CallID1, the transfer was not initiated.", username));
  942. throw new ApplicationException("Dual transfer was called with an empty value for CallID1, the transfer was not initiated.");
  943. }
  944. else if (callID2.IsNullOrBlank())
  945. {
  946. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Dual transfer was called with an empty value for CallID2, the transfer was not initiated.", username));
  947. throw new ApplicationException("Dual transfer was called with an empty value for CallID2, the transfer was not initiated.");
  948. }
  949. SIPDialogue dialogue1 = GetDialogueRelaxed(username, callID1);
  950. if (dialogue1 == null)
  951. {
  952. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "A dual transfer could not locate the dialogue for Call-ID " + callID1 + ".", username));
  953. throw new ApplicationException("A dual transfer could not be processed as no dialogue could be found for Call-ID " + callID1 + ".");
  954. }
  955. else if (dialogue1.Owner.ToLower() != username.ToLower())
  956. {
  957. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "A dual transfer could not be processed as dialogue 1 did not match the username provided.", username));
  958. throw new ApplicationException("A dual transfer could not be processed as a dialogue did not match the username provided.");
  959. }
  960. SIPDialogue dialogue2 = GetDialogueRelaxed(username, callID2);
  961. if (dialogue2 == null)
  962. {
  963. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "A dual transfer could not locate the dialogue for Call-ID " + callID2 + ".", username));
  964. throw new ApplicationException("A dual transfer could not be processed as no dialogue could be found for Call-ID " + callID2 + ".");
  965. }
  966. else if (dialogue2.Owner.ToLower() != username.ToLower())
  967. {
  968. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "A dual transfer could not be processed as dialogue 2 did not match the username provided.", username));
  969. throw new ApplicationException("A dual transfer could not be processed as a dialogue did not match the username provided.");
  970. }
  971. else if (dialogue1 == dialogue2)
  972. {
  973. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "A dual transfer could not be processed as a the callid's resolved to the same dialogue.", username));
  974. throw new ApplicationException("A dual transfer could not be processed as a the callid's resolved to the same dialogue.");
  975. }
  976. SIPDialogue oppositeDialogue1 = GetOppositeDialogue(dialogue1);
  977. if (oppositeDialogue1 == null)
  978. {
  979. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "A dual transfer could not locate the opposing dialogue for Call-ID " + callID1 + ".", username));
  980. throw new ApplicationException("A dual transfer could not be processed as the opposing dialogue could be found for Call-ID " + callID1 + ".");
  981. }
  982. SIPDialogue oppositeDialogue2 = GetOppositeDialogue(dialogue2);
  983. if (oppositeDialogue2 == null)
  984. {
  985. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "A dual transfer could not locate the opposing dialogue for Call-ID " + callID2 + ".", username));
  986. throw new ApplicationException("A dual transfer could not be processed as the opposing dialogue could be found for Call-ID " + callID2 + ".");
  987. }
  988. Guid newBridgeId = Guid.NewGuid();
  989. oppositeDialogue1.BridgeId = newBridgeId;
  990. oppositeDialogue2.BridgeId = newBridgeId;
  991. m_sipDialoguePersistor.UpdateProperty(oppositeDialogue1.Id, "BridgeID", newBridgeId.ToString());
  992. m_sipDialoguePersistor.UpdateProperty(oppositeDialogue2.Id, "BridgeID", newBridgeId.ToString());
  993. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Dual transfer re-inviting dialogues " + oppositeDialogue1.DialogueName + " and " + oppositeDialogue2.DialogueName + ".", username));
  994. ReInvite(oppositeDialogue1, oppositeDialogue2);
  995. Log_External(new SIPMonitorMachineEvent(SIPMonitorMachineEventTypesEnum.SIPDialogueUpdated, oppositeDialogue1.Owner, oppositeDialogue1.Id.ToString(), oppositeDialogue1.LocalUserField.URI));
  996. Log_External(new SIPMonitorMachineEvent(SIPMonitorMachineEventTypesEnum.SIPDialogueUpdated, oppositeDialogue2.Owner, oppositeDialogue2.Id.ToString(), oppositeDialogue2.LocalUserField.URI));
  997. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Dual transfer hanging up dialogue " + dialogue1.DialogueName + " Call-ID " + dialogue1.CallId + ".", username));
  998. dialogue1.Hangup(m_sipTransport, m_outboundProxy);
  999. CallHungup(dialogue1, "Attended transfer", false);
  1000. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Dual transfer hanging up dialogue " + dialogue2.DialogueName + " Call-ID " + dialogue2.CallId + ".", username));
  1001. dialogue2.Hangup(m_sipTransport, m_outboundProxy);
  1002. CallHungup(dialogue2, "Attended transfer", false);
  1003. Log_External(new SIPMonitorMachineEvent(SIPMonitorMachineEventTypesEnum.SIPDialogueRemoved, dialogue1.Owner, dialogue1.Id.ToString(), dialogue1.LocalUserField.URI));
  1004. Log_External(new SIPMonitorMachineEvent(SIPMonitorMachineEventTypesEnum.SIPDialogueRemoved, dialogue2.Owner, dialogue2.Id.ToString(), dialogue2.LocalUserField.URI));
  1005. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Dual transfer for Call-ID's " + callID1 + " and " + callID2 + " was successfully completed.", username));
  1006. }
  1007. catch (Exception excp)
  1008. {
  1009. logger.Error("Exception DualTransfer. " + excp.Message);
  1010. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Error processing a dual transfer. " + excp.Message, username));
  1011. throw;
  1012. }
  1013. }
  1014. private void InDialogueTransactionInfoResponseReceived(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPTransaction sipTransaction, SIPResponse sipResponse)
  1015. {
  1016. SIPDialogue dialogue = GetDialogue(sipResponse.Header.CallId, sipResponse.Header.From.FromTag, sipResponse.Header.To.ToTag);
  1017. string owner = (dialogue != null) ? dialogue.Owner : null;
  1018. try
  1019. {
  1020. // Lookup the originating transaction.
  1021. SIPTransaction originTransaction = m_sipTransport.GetTransaction(m_inDialogueTransactions[sipTransaction.TransactionId]);
  1022. SIPResponse response = sipResponse.Copy();
  1023. response.Header.Vias = originTransaction.TransactionRequest.Header.Vias;
  1024. response.Header.To = originTransaction.TransactionRequest.Header.To;
  1025. response.Header.From = originTransaction.TransactionRequest.Header.From;
  1026. response.Header.CallId = originTransaction.TransactionRequest.Header.CallId;
  1027. response.Header.CSeq = originTransaction.TransactionRequest.Header.CSeq;
  1028. response.Header.Contact = SIPContactHeader.CreateSIPContactList(new SIPURI(originTransaction.TransactionRequest.URI.Scheme, localSIPEndPoint));
  1029. response.Header.RecordRoutes = null; // Can't change route set within a dialogue.
  1030. response.Header.UserAgent = m_userAgentString;
  1031. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Forwarding in dialogue response from " + remoteEndPoint + " " + sipResponse.Header.CSeqMethod + " " + sipResponse.StatusCode + " " + sipResponse.ReasonPhrase + " to " + response.Header.Vias.TopViaHeader.ReceivedFromAddress + ".", owner));
  1032. // Forward the response back to the requester.
  1033. originTransaction.SendInformationalResponse(response);
  1034. }
  1035. catch (Exception excp)
  1036. {
  1037. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.Error, "Exception processing in dialogue " + sipResponse.Header.CSeqMethod + " info response. " + excp.Message, owner));
  1038. }
  1039. }
  1040. private void InDialogueTransactionFinalResponseReceived(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPTransaction sipTransaction, SIPResponse sipResponse)
  1041. {
  1042. SIPDialogue dialogue = GetDialogue(sipResponse.Header.CallId, sipResponse.Header.From.FromTag, sipResponse.Header.To.ToTag);
  1043. string owner = (dialogue != null) ? dialogue.Owner : null;
  1044. try
  1045. {
  1046. logger.Debug("Final response of " + sipResponse.StatusCode + " on " + sipResponse.Header.CSeqMethod + " in-dialogue transaction.");
  1047. // Lookup the originating transaction.
  1048. SIPTransaction originTransaction = m_sipTransport.GetTransaction(m_inDialogueTransactions[sipTransaction.TransactionId]);
  1049. IPAddress remoteUAIPAddress = (sipResponse.Header.ProxyReceivedFrom.IsNullOrBlank()) ? remoteEndPoint.Address : SIPEndPoint.ParseSIPEndPoint(sipResponse.Header.ProxyReceivedFrom).Address;
  1050. SIPEndPoint forwardSIPEndPoint = m_sipTransport.GetDefaultSIPEndPoint(sipResponse.Header.Vias.TopViaHeader.Transport);
  1051. SIPResponse response = sipResponse.Copy();
  1052. response.Header.Vias = originTransaction.TransactionRequest.Header.Vias;
  1053. response.Header.To = originTransaction.TransactionRequest.Header.To;
  1054. response.Header.From = originTransaction.TransactionRequest.Header.From;
  1055. response.Header.CallId = originTransaction.TransactionRequest.Header.CallId;
  1056. response.Header.CSeq = originTransaction.TransactionRequest.Header.CSeq;
  1057. response.Header.Contact = new List<SIPContactHeader>() { new SIPContactHeader(null, new SIPURI(originTransaction.TransactionRequest.URI.Scheme, forwardSIPEndPoint)) };
  1058. response.Header.RecordRoutes = null; // Can't change route set within a dialogue.
  1059. response.Header.UserAgent = m_userAgentString;
  1060. if (sipResponse.Body != null && sipResponse.Header.CSeqMethod == SIPMethodsEnum.INVITE)
  1061. {
  1062. bool wasMangled = false;
  1063. response.Body = SIPPacketMangler.MangleSDP(sipResponse.Body, remoteUAIPAddress.ToString(), out wasMangled);
  1064. response.Header.ContentLength = response.Body.Length;
  1065. }
  1066. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Forwarding in dialogue response from " + remoteEndPoint + " " + sipResponse.Header.CSeqMethod + " final response " + sipResponse.StatusCode + " " + sipResponse.ReasonPhrase + " to " + response.Header.Vias.TopViaHeader.ReceivedFromAddress + ".", owner));
  1067. // Forward the response back to the requester.
  1068. originTransaction.SendFinalResponse(response);
  1069. }
  1070. catch (Exception excp)
  1071. {
  1072. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.Error, "Exception processing in dialogue " + sipResponse.Header.CSeqMethod + " final response. " + excp.Message, owner));
  1073. }
  1074. }
  1075. private void InDialogueTransactionRemoved(SIPTransaction sipTransaction)
  1076. {
  1077. try
  1078. {
  1079. if (m_inDialogueTransactions.ContainsKey(sipTransaction.TransactionId))
  1080. {
  1081. lock (m_inDialogueTransactions)
  1082. {
  1083. m_inDialogueTransactions.Remove(sipTransaction.TransactionId);
  1084. }
  1085. }
  1086. }
  1087. catch (Exception excp)
  1088. {
  1089. logger.Error("Exception InDialogueTransactionStateChanged. " + excp);
  1090. }
  1091. }
  1092. private SIPRequest GetInviteRequest(SIPDialogue dialogue, SIPEndPoint localSIPEndPoint, string body)
  1093. {
  1094. SIPRequest inviteRequest = new SIPRequest(SIPMethodsEnum.INVITE, dialogue.RemoteTarget);
  1095. SIPHeader inviteHeader = new SIPHeader(SIPFromHeader.ParseFromHeader(dialogue.LocalUserField.ToString()), SIPToHeader.ParseToHeader(dialogue.RemoteUserField.ToString()), dialogue.CSeq, dialogue.CallId);
  1096. SIPURI contactURI = new SIPURI(dialogue.RemoteTarget.Scheme, localSIPEndPoint);
  1097. inviteHeader.Contact = SIPContactHeader.ParseContactHeader("<" + contactURI.ToString() + ">");
  1098. inviteHeader.CSeqMethod = SIPMethodsEnum.INVITE;
  1099. inviteRequest.Header = inviteHeader;
  1100. inviteRequest.Header.Routes = dialogue.RouteSet;
  1101. SIPViaHeader viaHeader = new SIPViaHeader(localSIPEndPoint, CallProperties.CreateBranchId());
  1102. inviteRequest.Header.Vias.PushViaHeader(viaHeader);
  1103. inviteRequest.Body = body;
  1104. inviteRequest.Header.ContentLength = body.Length;
  1105. inviteRequest.Header.ContentType = "application/sdp";
  1106. return inviteRequest;
  1107. }
  1108. private void SendNotifyRequestForRefer(SIPRequest referRequest, SIPDialogue referDialogue, SIPEndPoint localEndPoint, SIPResponseStatusCodesEnum responseCode, string responseReason)
  1109. {
  1110. try
  1111. {
  1112. //logger.Debug("Sending NOTIFY for refer subscription to " + referDialogue.RemoteTarget.ToParameterlessString() + ", status " + responseCode + " " + responseReason + ".");
  1113. referDialogue.CSeq++;
  1114. m_sipDialoguePersistor.UpdateProperty(referDialogue.Id, "CSeq", referDialogue.CSeq);
  1115. SIPRequest notifyTryingRequest = GetNotifyRequest(referRequest, referDialogue, new SIPResponse(responseCode, responseReason, null), localEndPoint);
  1116. SIPEndPoint forwardEndPoint = null;
  1117. SIPDNSLookupResult lookupResult = m_sipTransport.GetRequestEndPoint(notifyTryingRequest, m_outboundProxy, false);
  1118. if (lookupResult.LookupError != null)
  1119. {
  1120. logger.Warn("SendNotifyRequestForRefer Failed to resolve " + lookupResult.URI.Host + ".");
  1121. }
  1122. else
  1123. {
  1124. forwardEndPoint = lookupResult.GetSIPEndPoint();
  1125. }
  1126. SIPNonInviteTransaction notifyTryingTransaction = m_sipTransport.CreateNonInviteTransaction(notifyTryingRequest, forwardEndPoint, localEndPoint, m_outboundProxy);
  1127. notifyTryingTransaction.SendReliableRequest();
  1128. }
  1129. catch (Exception excp)
  1130. {
  1131. logger.Error("Exception SendNotifyRequestForRefer. " + excp.Message);
  1132. }
  1133. }
  1134. /// <summary>
  1135. /// Constructs a NOTIFY request to send within the implicit subscription created when processing a REFER request.
  1136. /// </summary>
  1137. /// <remarks>
  1138. /// From RFC 3515 2.4.5:
  1139. /// The body of a NOTIFY MUST begin with a SIP Response Status-Line...
  1140. /// </remarks>
  1141. /// <param name="referRequest">The REFER request that created the implicit refer event subscription.</param>
  1142. /// <param name="referDialogue">The dialogue that the REFER request has been received within.</param>
  1143. /// <param name="referResponse">The response that has been received to whatever is doing the post-REFER processing.</param>
  1144. /// <param name="localEndPoint">The local SIP end point that the NOTIFY request will be sent from.</param>
  1145. /// <returns>A NOTIFY request suitable for sending to the remote end of the REFER initiating dialogue.</returns>
  1146. private SIPRequest GetNotifyRequest(SIPRequest referRequest, SIPDialogue referDialogue, SIPResponse referResponse, SIPEndPoint localEndPoint)
  1147. {
  1148. try
  1149. {
  1150. SIPRequest notifyRequest = new SIPRequest(SIPMethodsEnum.NOTIFY, referRequest.Header.Contact[0].ContactURI);
  1151. notifyRequest.Header = new SIPHeader(SIPFromHeader.ParseFromHeader(referDialogue.LocalUserField.ToString()), SIPToHeader.ParseToHeader(referDialogue.RemoteUserField.ToString()), referDialogue.CSeq, referDialogue.CallId);
  1152. notifyRequest.Header.Event = m_referNotifyEventValue; // + ";id=" + referRequest.Header.CSeq;
  1153. notifyRequest.Header.CSeqMethod = SIPMethodsEnum.NOTIFY;
  1154. notifyRequest.Header.SubscriptionState = (referResponse.StatusCode >= 200) ? "terminated;reason=noresource" : "active;expires=60";
  1155. notifyRequest.Header.ContentType = m_referNotifyContentType;
  1156. SIPViaHeader viaHeader = new SIPViaHeader(localEndPoint, CallProperties.CreateBranchId());
  1157. notifyRequest.Header.Vias.PushViaHeader(viaHeader);
  1158. notifyRequest.Body = (referResponse.SIPVersion + " " + referResponse.StatusCode + " " + referResponse.ReasonPhrase).Trim();
  1159. notifyRequest.Header.ContentLength = notifyRequest.Body.Length;
  1160. return notifyRequest;
  1161. }
  1162. catch (Exception excp)
  1163. {
  1164. logger.Error("Exception GetNotifyRequest. " + excp.Message);
  1165. throw;
  1166. }
  1167. }
  1168. }
  1169. }