PageRenderTime 97ms CodeModel.GetById 30ms RepoModel.GetById 1ms app.codeStats 0ms

/sipsorcery-core/SIPSorcery.SIP.App/SIPUserAgents/SIPB2BUserAgent.cs

https://github.com/thecc4re/sipsorcery-mono
C# | 398 lines | 288 code | 58 blank | 52 comment | 33 complexity | cf960a5b89b85357921686e8def337af MD5 | raw file
Possible License(s): CC-BY-SA-3.0
  1. //-----------------------------------------------------------------------------
  2. // Filename: SIPB2BUserAgent.cs
  3. //
  4. // Description: Implementation of a SIP Back-to-back User Agent.
  5. //
  6. // History:
  7. // 21 Jul 2009 Aaron Clauson Created.
  8. //
  9. // License:
  10. // This software is licensed under the BSD License http://www.opensource.org/licenses/bsd-license.php
  11. //
  12. // Copyright (c) 2009 Aaron Clauson (aaronc@blueface.ie), Blue Face Ltd, Dublin, Ireland (www.blueface.ie)
  13. // All rights reserved.
  14. //
  15. // Redistribution and use in source and binary forms, with or without modification, are permitted provided that
  16. // the following conditions are met:
  17. //
  18. // Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  19. // Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
  20. // disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of Blue Face Ltd.
  21. // nor the names of its contributors may be used to endorse or promote products derived from this software without specific
  22. // prior written permission.
  23. //
  24. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
  25. // BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  26. // IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
  27. // OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
  28. // OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  29. // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  30. // POSSIBILITY OF SUCH DAMAGE.
  31. //-----------------------------------------------------------------------------
  32. using System;
  33. using System.Collections.Generic;
  34. using System.Linq;
  35. using System.Net;
  36. using System.Text;
  37. using log4net;
  38. using SIPSorcery.Sys;
  39. namespace SIPSorcery.SIP.App
  40. {
  41. /// <summary>
  42. /// This class represents a back-to-back (B2B) user agent (UA) that is used to attach an outgoing
  43. /// call (UAC) to an incoming (UAS) call. Normally the UAC call would be the client side of a call that
  44. /// is placed to an external UAS in this case it's the client side of a call to a UAS in the same process.
  45. /// The use for this class is to allow an outgoing call from a SIP Account to another SIP Account's incoming
  46. /// dial plan.
  47. /// </summary>
  48. public class SIPB2BUserAgent : ISIPServerUserAgent, ISIPClientUserAgent
  49. {
  50. private static ILog logger = AppState.logger;
  51. private static readonly SIPEndPoint m_blackhole = new SIPEndPoint(new IPEndPoint(SIPTransport.BlackholeAddress, 0));
  52. private SIPMonitorLogDelegate Log_External;
  53. private QueueNewCallDelegate QueueNewCall_External;
  54. private SIPTransport m_sipTransport;
  55. // UAC fields.
  56. private string m_uacOwner;
  57. private string m_uacAdminMemberId;
  58. private UACInviteTransaction m_uacTransaction;
  59. private SIPCallDescriptor m_uacCallDescriptor;
  60. public string Owner { get { return m_uacOwner; } }
  61. public string AdminMemberId { get { return m_uacAdminMemberId; } }
  62. public UACInviteTransaction ServerTransaction { get { return m_uacTransaction; } }
  63. public SIPCallDescriptor CallDescriptor { get { return m_uacCallDescriptor; } }
  64. public event SIPCallResponseDelegate CallTrying;
  65. public event SIPCallResponseDelegate CallRinging;
  66. public event SIPCallResponseDelegate CallAnswered;
  67. public event SIPCallFailedDelegate CallFailed;
  68. // UAS fields.
  69. private UASInviteTransaction m_uasTransaction;
  70. public bool IsB2B { get { return true; } }
  71. public bool IsAuthenticated { get { return false; } set { } }
  72. public SIPCallDirection CallDirection { get { return SIPCallDirection.In; } }
  73. public UASInviteTransaction SIPTransaction
  74. {
  75. get { return m_uasTransaction; }
  76. }
  77. public SIPAccount SIPAccount
  78. {
  79. get { return m_uacCallDescriptor.ToSIPAccount; }
  80. set { }
  81. }
  82. public SIPRequest CallRequest
  83. {
  84. get { return m_uasTransaction.TransactionRequest; }
  85. }
  86. public string CallDestination
  87. {
  88. get { return m_uasTransaction.TransactionRequest.URI.User; }
  89. }
  90. public bool IsUASAnswered
  91. {
  92. get { return m_uasTransaction != null && m_uacTransaction.TransactionFinalResponse != null; }
  93. }
  94. public bool IsUACAnswered
  95. {
  96. get { return m_uacTransaction != null && m_uacTransaction.TransactionFinalResponse != null; }
  97. }
  98. public event SIPUASDelegate CallCancelled;
  99. public event SIPUASDelegate NoRingTimeout;
  100. public event SIPUASDelegate TransactionComplete;
  101. public event SIPUASStateChangedDelegate UASStateChanged;
  102. // UAS and UAC field.
  103. private SIPDialogue m_sipDialogue;
  104. public SIPDialogue SIPDialogue { get { return m_sipDialogue; } }
  105. //private SIPAccount m_destinationSIPAccount;
  106. public SIPB2BUserAgent(
  107. SIPMonitorLogDelegate logDelegate,
  108. QueueNewCallDelegate queueCall,
  109. SIPTransport sipTranpsort,
  110. string uacOwner,
  111. string uacAdminMemberId
  112. )
  113. {
  114. Log_External = logDelegate;
  115. QueueNewCall_External = queueCall;
  116. m_sipTransport = sipTranpsort;
  117. m_uacOwner = uacOwner;
  118. m_uacAdminMemberId = uacAdminMemberId;
  119. }
  120. #region UAC methods.
  121. public void Call(SIPCallDescriptor sipCallDescriptor)
  122. {
  123. try
  124. {
  125. m_uacCallDescriptor = sipCallDescriptor;
  126. SIPRequest uacInviteRequest = GetInviteRequest(m_uacCallDescriptor.Uri, sipCallDescriptor.GetFromHeader());
  127. if (sipCallDescriptor.MangleResponseSDP && sipCallDescriptor.MangleIPAddress != null)
  128. {
  129. uacInviteRequest.Header.ProxyReceivedFrom = sipCallDescriptor.MangleIPAddress.ToString();
  130. }
  131. uacInviteRequest.Body = sipCallDescriptor.Content;
  132. uacInviteRequest.Header.ContentType = sipCallDescriptor.ContentType;
  133. uacInviteRequest.LocalSIPEndPoint = m_blackhole;
  134. uacInviteRequest.RemoteSIPEndPoint = m_blackhole;
  135. // Now that we have a destination socket create a new UAC transaction for forwarded leg of the call.
  136. m_uacTransaction = m_sipTransport.CreateUACTransaction(uacInviteRequest, m_blackhole, m_blackhole, null);
  137. if (m_uacTransaction.CDR != null)
  138. {
  139. m_uacTransaction.CDR.Owner = m_uacOwner;
  140. m_uacTransaction.CDR.AdminMemberId = m_uacAdminMemberId;
  141. }
  142. //uacTransaction.UACInviteTransactionInformationResponseReceived += ServerInformationResponseReceived;
  143. //uacTransaction.UACInviteTransactionFinalResponseReceived += ServerFinalResponseReceived;
  144. //uacTransaction.UACInviteTransactionTimedOut += ServerTimedOut;
  145. //uacTransaction.TransactionTraceMessage += TransactionTraceMessage;
  146. m_uacTransaction.SendInviteRequest(m_blackhole, m_uacTransaction.TransactionRequest);
  147. SIPRequest uasInviteRequest = uacInviteRequest.Copy();
  148. uasInviteRequest.LocalSIPEndPoint = m_blackhole;
  149. uasInviteRequest.RemoteSIPEndPoint = m_blackhole;
  150. uasInviteRequest.Header.Vias.TopViaHeader.Branch = CallProperties.CreateBranchId();
  151. m_uasTransaction = m_sipTransport.CreateUASTransaction(uasInviteRequest, m_blackhole, m_blackhole, null);
  152. SetOwner(sipCallDescriptor.ToSIPAccount.Owner, sipCallDescriptor.ToSIPAccount.AdminMemberId);
  153. //m_uasTransaction.TransactionTraceMessage += TransactionTraceMessage;
  154. //m_uasTransaction.UASInviteTransactionTimedOut += ClientTimedOut;
  155. //m_uasTransaction.UASInviteTransactionCancelled += (t) => { };
  156. QueueNewCall_External(this);
  157. }
  158. catch (Exception excp)
  159. {
  160. logger.Error("Exception SIPB2BUserAgent Call. " + excp.Message);
  161. }
  162. }
  163. public void Cancel()
  164. {
  165. try
  166. {
  167. logger.Debug("SIPB2BUserAgent Cancel.");
  168. m_uasTransaction.CancelCall();
  169. m_uacTransaction.CancelCall();
  170. if (CallCancelled != null)
  171. {
  172. CallCancelled(this);
  173. }
  174. }
  175. catch (Exception excp)
  176. {
  177. logger.Error("Exception SIPB2BUserAgent Cancel. " + excp.Message);
  178. }
  179. }
  180. public void Update(CRMHeaders crmHeaders)
  181. {
  182. throw new NotImplementedException();
  183. }
  184. #endregion
  185. #region UAS methods.
  186. public void SetTraceDelegate(SIPTransactionTraceMessageDelegate traceDelegate)
  187. {
  188. m_uasTransaction.TransactionTraceMessage += traceDelegate;
  189. }
  190. public bool LoadSIPAccountForIncomingCall()
  191. {
  192. return true;
  193. }
  194. public bool AuthenticateCall()
  195. {
  196. return false;
  197. }
  198. public void Progress(SIPResponseStatusCodesEnum progressStatus, string reasonPhrase, string[] customHeaders, string progressContentType, string progressBody)
  199. {
  200. try
  201. {
  202. if (!IsUASAnswered)
  203. {
  204. if ((int)progressStatus >= 200)
  205. {
  206. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "B2BUA call was passed an invalid response status of " + (int)progressStatus + ", ignoring.", m_uacOwner));
  207. }
  208. else
  209. {
  210. if (UASStateChanged != null)
  211. {
  212. UASStateChanged(this, progressStatus, reasonPhrase);
  213. }
  214. if (m_uasTransaction.TransactionState == SIPTransactionStatesEnum.Proceeding)
  215. {
  216. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "B2BUA call ignoring progress response with status of " + (int)progressStatus + " as already in " + m_uasTransaction.TransactionState + ".", m_uacOwner));
  217. }
  218. else
  219. {
  220. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "B2BUA call progressing with " + progressStatus + ".", m_uacOwner));
  221. SIPResponse uasProgressResponse = SIPTransport.GetResponse(m_uasTransaction.TransactionRequest, progressStatus, reasonPhrase);
  222. m_uasTransaction.SendInformationalResponse(uasProgressResponse);
  223. SIPResponse uacProgressResponse = SIPTransport.GetResponse(m_uacTransaction.TransactionRequest, progressStatus, reasonPhrase);
  224. if (!progressBody.IsNullOrBlank())
  225. {
  226. uacProgressResponse.Body = progressBody;
  227. uacProgressResponse.Header.ContentType = progressContentType;
  228. }
  229. if (customHeaders != null && customHeaders.Length > 0)
  230. {
  231. foreach (string header in customHeaders)
  232. {
  233. uacProgressResponse.Header.UnknownHeaders.Add(header);
  234. }
  235. }
  236. m_uacTransaction.GotResponse(m_blackhole, m_blackhole, uacProgressResponse);
  237. CallRinging((ISIPClientUserAgent)this, uacProgressResponse);
  238. }
  239. }
  240. }
  241. else
  242. {
  243. logger.Warn("B2BUserAgent Progress fired on already answered call.");
  244. }
  245. }
  246. catch (Exception excp)
  247. {
  248. logger.Error("Exception B2BUserAgent Progress. " + excp.Message);
  249. }
  250. }
  251. public SIPDialogue Answer(string contentType, string body, string toTag, SIPDialogue answeredDialogue, SIPDialogueTransferModesEnum transferMode)
  252. {
  253. return Answer(contentType, body, answeredDialogue, transferMode);
  254. }
  255. public SIPDialogue Answer(string contentType, string body, SIPDialogue answeredDialogue, SIPDialogueTransferModesEnum transferMode)
  256. {
  257. try
  258. {
  259. logger.Debug("SIPB2BUserAgent Answer.");
  260. m_sipDialogue = answeredDialogue;
  261. if (UASStateChanged != null)
  262. {
  263. UASStateChanged(this, SIPResponseStatusCodesEnum.Ok, null);
  264. }
  265. SIPResponse uasOkResponse = SIPTransport.GetResponse(m_uasTransaction.TransactionRequest, SIPResponseStatusCodesEnum.Ok, null);
  266. m_uasTransaction.SendFinalResponse(uasOkResponse);
  267. m_uasTransaction.ACKReceived(m_blackhole, m_blackhole, null);
  268. SIPResponse uacOkResponse = SIPTransport.GetResponse(m_uacTransaction.TransactionRequest, SIPResponseStatusCodesEnum.Ok, null);
  269. uacOkResponse.Header.Contact = new List<SIPContactHeader>() { new SIPContactHeader(null, new SIPURI(SIPSchemesEnum.sip, m_blackhole)) };
  270. m_uacTransaction.GotResponse(m_blackhole, m_blackhole, uacOkResponse);
  271. uacOkResponse.Header.ContentType = contentType;
  272. if (!body.IsNullOrBlank())
  273. {
  274. uacOkResponse.Body = body;
  275. uacOkResponse.Header.ContentLength = body.Length;
  276. }
  277. CallAnswered((ISIPClientUserAgent)this, uacOkResponse);
  278. return null;
  279. }
  280. catch (Exception excp)
  281. {
  282. logger.Error("Exception SIPB2BUSerAgent Answer. " + excp.Message);
  283. throw;
  284. }
  285. }
  286. public void Reject(SIPResponseStatusCodesEnum rejectCode, string rejectReason, string[] customHeaders)
  287. {
  288. logger.Debug("SIPB2BUserAgent Reject.");
  289. if (UASStateChanged != null)
  290. {
  291. UASStateChanged(this, rejectCode, rejectReason);
  292. }
  293. SIPResponse uasfailureResponse = SIPTransport.GetResponse(m_uasTransaction.TransactionRequest, rejectCode, rejectReason);
  294. m_uasTransaction.SendFinalResponse(uasfailureResponse);
  295. SIPResponse uacfailureResponse = SIPTransport.GetResponse(m_uacTransaction.TransactionRequest, rejectCode, rejectReason);
  296. if (customHeaders != null && customHeaders.Length > 0)
  297. {
  298. foreach (string header in customHeaders)
  299. {
  300. uacfailureResponse.Header.UnknownHeaders.Add(header);
  301. }
  302. }
  303. m_uacTransaction.GotResponse(m_blackhole, m_blackhole, uacfailureResponse);
  304. CallAnswered((ISIPClientUserAgent)this, uacfailureResponse);
  305. }
  306. public void Redirect(SIPResponseStatusCodesEnum redirectCode, SIPURI redirectURI)
  307. {
  308. logger.Debug("SIPB2BUserAgent Redirect.");
  309. //m_uas.Redirect(redirectCode, redirectURI);
  310. }
  311. public void NoCDR()
  312. {
  313. m_uasTransaction.CDR = null;
  314. m_uacTransaction.CDR = null;
  315. }
  316. public void SetOwner(string owner, string adminMemberId)
  317. {
  318. if (m_uasTransaction.CDR != null)
  319. {
  320. m_uasTransaction.CDR.Owner = owner;
  321. m_uasTransaction.CDR.AdminMemberId = adminMemberId;
  322. }
  323. }
  324. #endregion
  325. private SIPRequest GetInviteRequest(string callURI, SIPFromHeader fromHeader)
  326. {
  327. SIPRequest inviteRequest = new SIPRequest(SIPMethodsEnum.INVITE, SIPURI.ParseSIPURI(callURI));
  328. inviteRequest.LocalSIPEndPoint = m_blackhole;
  329. SIPHeader inviteHeader = new SIPHeader(fromHeader, new SIPToHeader(null, inviteRequest.URI, null), 1, CallProperties.CreateNewCallId());
  330. inviteHeader.From.FromTag = CallProperties.CreateNewTag();
  331. // For incoming calls forwarded via the dial plan the username needs to go into the Contact header.
  332. inviteHeader.Contact = new List<SIPContactHeader>() { new SIPContactHeader(null, new SIPURI(inviteRequest.URI.Scheme, m_blackhole)) };
  333. inviteHeader.CSeqMethod = SIPMethodsEnum.INVITE;
  334. inviteRequest.Header = inviteHeader;
  335. SIPViaHeader viaHeader = new SIPViaHeader(m_blackhole, CallProperties.CreateBranchId());
  336. inviteRequest.Header.Vias.PushViaHeader(viaHeader);
  337. return inviteRequest;
  338. }
  339. }
  340. }