PageRenderTime 47ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/sipsorcery-core/SIPSorcery.AppServer.DialPlan/DialPlanApps/ForkCall.cs

https://github.com/thecc4re/sipsorcery-mono
C# | 663 lines | 466 code | 61 blank | 136 comment | 100 complexity | 1d23e2b8e19764e037d9cad5111ff896 MD5 | raw file
Possible License(s): CC-BY-SA-3.0
  1. //-----------------------------------------------------------------------------
  2. // Filename: ForkCall.cs
  3. //
  4. // Description: A dial plan command that facilitates forked calls.
  5. //
  6. // History:
  7. // 07 Feb 2008 Aaron Clauson Created.
  8. // 17 Apr 2008 Aaron Clauson Added tracing.
  9. // 10 Aug 2008 Aaron Clauson Moved the call resolution functionality to SIPCallResolver.
  10. //
  11. // License:
  12. // This software is licensed under the BSD License http://www.opensource.org/licenses/bsd-license.php
  13. //
  14. // Copyright (c) 2008 Aaron Clauson (aaronc@blueface.ie), Blue Face Ltd, Dublin, Ireland (www.blueface.ie)
  15. // All rights reserved.
  16. //
  17. // Redistribution and use in source and binary forms, with or without modification, are permitted provided that
  18. // the following conditions are met:
  19. //
  20. // Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  21. // Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
  22. // disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of Blue Face Ltd.
  23. // nor the names of its contributors may be used to endorse or promote products derived from this software without specific
  24. // prior written permission.
  25. //
  26. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
  27. // BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  28. // IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
  29. // OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
  30. // OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  31. // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  32. // POSSIBILITY OF SUCH DAMAGE.
  33. //-----------------------------------------------------------------------------
  34. using System;
  35. using System.Collections.Generic;
  36. using System.Data;
  37. using System.IO;
  38. using System.Linq;
  39. using System.Linq.Expressions;
  40. using System.Net;
  41. using System.Security;
  42. using System.Text;
  43. using System.Text.RegularExpressions;
  44. using System.Threading;
  45. using SIPSorcery.SIP;
  46. using SIPSorcery.SIP.App;
  47. using SIPSorcery.Sys;
  48. using log4net;
  49. #if UNITTEST
  50. using NUnit.Framework;
  51. #endif
  52. namespace SIPSorcery.AppServer.DialPlan
  53. {
  54. public class ForkCall
  55. {
  56. private const string THEAD_NAME = "forkcall-";
  57. public const int MAX_CALLS_PER_LEG = 10;
  58. public const int MAX_DELAY_SECONDS = 120;
  59. private const string ALLFORWARDS_FAILED_REASONPHRASE = "All forwards failed";
  60. private static ILog logger = AppState.logger;
  61. private SIPTransport m_sipTransport;
  62. private ISIPCallManager m_callManager;
  63. private event SIPMonitorLogDelegate m_statefulProxyLogEvent; // Used to send log messages back to the application server core.
  64. private QueueNewCallDelegate QueueNewCall_External; // Function delegate to allow new calls to be placed on teh call manager and run through the dialplan logic.
  65. private DialStringParser m_dialStringParser; // Used to create a new list of calls if a redirect response is received to a fork call leg.
  66. private string m_username; // The call owner.
  67. private string m_adminMemberId;
  68. private SIPEndPoint m_outboundProxySocket; // If this app forwards calls via an outbound proxy this value will be set.
  69. private SIPResponseStatusCodesEnum m_lastFailureStatus; // If the call fails the first leg that returns an error will be used as the reason on the error response.
  70. private string m_lastFailureReason;
  71. private bool m_callAnswered; // Set to true once the first Ok response has been received from a forwarded call leg.
  72. private bool m_commandCancelled;
  73. private ISIPClientUserAgent m_answeredUAC;
  74. internal event CallProgressDelegate CallProgress;
  75. internal event CallFailedDelegate CallFailed;
  76. internal event CallAnsweredDelegate CallAnswered;
  77. private Queue<List<SIPCallDescriptor>> m_priorityCallsQueue = new Queue<List<SIPCallDescriptor>>(); // A queue of mulitple call legs that will be attempted until the call is answered ot there are none left.
  78. private List<ISIPClientUserAgent> m_switchCalls = new List<ISIPClientUserAgent>(); // Holds the multiple forwards that are currently being attempted.
  79. private List<SIPTransaction> m_switchCallTransactions = new List<SIPTransaction>(); // Used to maintain a list of all the transactions used in the call. Allows response codes and such to be checked.
  80. private List<SIPCallDescriptor> m_delayedCalls = new List<SIPCallDescriptor>();
  81. public SIPResponse AnsweredSIPResponse { get; private set; }
  82. /// <remarks>
  83. /// The ForkCall allows a SIP call to be forked to multiple destinations. To do this it utilises multiple
  84. /// simultaneous SIPCallDescriptor objects and consolidates their responses to work out what should and shouldn't
  85. /// be forwarded onto the client that initiated the call. The ForkCall acts as a classic SIP forking proxy.
  86. ///
  87. /// The ForkCall is capable of both multiple forwards and also of follow on forwarding in the event of a call
  88. /// leg of multiple forwards not succeeding. As an example:
  89. ///
  90. /// Dial(provider1&provider2|provider3&provider4|provider5&provider6)
  91. ///
  92. /// The handling of this call would be:
  93. /// 1. The call would be simultaneously forwarded to provider1 and provider2,
  94. /// 2. If the call was not successfully answered in step 1 the call would be simultaneously forwarded to provider3 and provider4,
  95. /// 3. If the call was not successfully answered in step 2 the call would be simultaneously forwarded to provider5 and provider6,
  96. /// 4. If the call was not successfully answered in step 3 the client call would be sent an error response.
  97. /// 5. If the client cancels the call at any time during the call all forwarding operations will halt.
  98. /// </remarks>
  99. /// <param name="sipTransport">The SIP transport layer that will handle the forked calls.</param>
  100. /// <param name="statefulProxyLogEvent">A delegate that allows the owning object to receive notifications from the ForkCall.</param>
  101. /// <param name="queueNewCall">A delegate that can be used to queue a new call with the SIP application server call manager. This
  102. /// delegate is used when a fork call generates a B2B call that requires the incoming dialplan for a called user to be processed.</param>
  103. /// <param name="dialStringParser">The dial string parser is used when a redirect response is received on a forked call leg. The
  104. /// parser can then be applied to the redirect SIP URI to generate new call legs to be added to the ForkCall.</param>
  105. /// <param name="username">The username of the call owner.</param>
  106. /// <param name="adminMemberId">The admin ID of the call owner.</param>
  107. /// <param name="outboundProxy">The outbound proxy to use for all SIP traffic originated. Can be null if an outbound proxy is not
  108. /// being used.</param>
  109. public ForkCall(
  110. SIPTransport sipTransport,
  111. SIPMonitorLogDelegate statefulProxyLogEvent,
  112. QueueNewCallDelegate queueNewCall,
  113. DialStringParser dialStringParser,
  114. string username,
  115. string adminMemberId,
  116. SIPEndPoint outboundProxy,
  117. ISIPCallManager callManager)
  118. {
  119. m_sipTransport = sipTransport;
  120. m_statefulProxyLogEvent = statefulProxyLogEvent;
  121. QueueNewCall_External = queueNewCall;
  122. m_dialStringParser = dialStringParser;
  123. m_username = username;
  124. m_adminMemberId = adminMemberId;
  125. m_outboundProxySocket = outboundProxy;
  126. m_callManager = callManager;
  127. }
  128. /// <summary>
  129. /// See overload.
  130. /// </summary>
  131. /// <param name="switchCallTransactions">An empty list that will be filled with transactions that the ForkCall creates and that each
  132. /// represent an outgoing call. The calling object can use the list to check response codes to determine the result of each leg in the
  133. /// ForkCall.</param>
  134. public ForkCall(
  135. SIPTransport sipTransport,
  136. SIPMonitorLogDelegate statefulProxyLogEvent,
  137. QueueNewCallDelegate queueNewCall,
  138. DialStringParser dialStringParser,
  139. string username,
  140. string adminMemberId,
  141. SIPEndPoint outboundProxy,
  142. ISIPCallManager callManager,
  143. out List<SIPTransaction> switchCallTransactions) :
  144. this(sipTransport, statefulProxyLogEvent, queueNewCall, dialStringParser, username, adminMemberId, outboundProxy, callManager)
  145. {
  146. switchCallTransactions = m_switchCallTransactions;
  147. }
  148. /// <summary>
  149. /// Starts a call based on multiple forking call legs. As each call leg fails the next leg is popped off the queue and attempted.
  150. /// </summary>
  151. /// <param name="callsQueue"></param>
  152. public void Start(Queue<List<SIPCallDescriptor>> callsQueue)
  153. {
  154. if (callsQueue == null || callsQueue.Count == 0)
  155. {
  156. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "No callable destinations were provided in Dial command, returning.", m_username));
  157. m_lastFailureStatus = SIPResponseStatusCodesEnum.InternalServerError;
  158. m_lastFailureReason = "Call list was empty";
  159. CallLegCompleted();
  160. }
  161. else
  162. {
  163. m_priorityCallsQueue = callsQueue;
  164. List<SIPCallDescriptor> calls = m_priorityCallsQueue.Dequeue();
  165. Start(calls);
  166. }
  167. }
  168. /// <summary>
  169. /// Starts a call based on a single multi forward call leg.
  170. /// </summary>
  171. /// <param name="calls">The list of simultaneous forwards to attempt.</param>
  172. public void Start(List<SIPCallDescriptor> callDescriptors)
  173. {
  174. if (callDescriptors != null && callDescriptors.Count > 0)
  175. {
  176. for (int index = 0; index < callDescriptors.Count; index++)
  177. {
  178. int availableThreads = 0;
  179. int ioCompletionThreadsAvailable = 0;
  180. ThreadPool.GetAvailableThreads(out availableThreads, out ioCompletionThreadsAvailable);
  181. if (availableThreads <= 0)
  182. {
  183. logger.Warn("The ThreadPool had no threads available in the pool to start a ForkCall leg, task will be queued.");
  184. }
  185. SIPCallDescriptor callDescriptor = callDescriptors[index];
  186. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "ForkCall commencing call leg to " + callDescriptor.Uri + ".", m_username));
  187. ThreadPool.QueueUserWorkItem(delegate { StartNewCallAsync(callDescriptor); });
  188. }
  189. }
  190. else
  191. {
  192. CallLegCompleted();
  193. }
  194. }
  195. private void StartNewCallAsync(SIPCallDescriptor callDescriptor)
  196. {
  197. try
  198. {
  199. if (Thread.CurrentThread.Name == null)
  200. {
  201. Thread.CurrentThread.Name = THEAD_NAME + DateTime.Now.ToString("HHmmss") + "-" + Crypto.GetRandomString(3);
  202. }
  203. StartNewCallSync(callDescriptor);
  204. }
  205. catch (Exception excp)
  206. {
  207. logger.Error("Exception StartNewCallAsync. " + excp.Message);
  208. }
  209. }
  210. private void StartNewCallSync(SIPCallDescriptor callDescriptor)
  211. {
  212. try
  213. {
  214. // A call will not be delayed if there are no other calls being attempted.
  215. if (callDescriptor.DelaySeconds != 0 && m_switchCalls.Count > 0)
  216. {
  217. callDescriptor.DelayMRE = new ManualResetEvent(false);
  218. lock (m_delayedCalls)
  219. {
  220. m_delayedCalls.Add(callDescriptor);
  221. }
  222. int delaySeconds = (callDescriptor.DelaySeconds > MAX_DELAY_SECONDS) ? MAX_DELAY_SECONDS : callDescriptor.DelaySeconds;
  223. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Delaying call leg to " + callDescriptor.Uri + " by " + delaySeconds + "s.", m_username));
  224. callDescriptor.DelayMRE.WaitOne(delaySeconds * 1000);
  225. }
  226. lock (m_delayedCalls)
  227. {
  228. m_delayedCalls.Remove(callDescriptor);
  229. }
  230. if (!m_callAnswered && !m_commandCancelled)
  231. {
  232. ISIPClientUserAgent uacCall = null;
  233. if (callDescriptor.ToSIPAccount == null)
  234. {
  235. if (callDescriptor.IsGoogleVoiceCall)
  236. {
  237. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Creating Google Voice user agent for " + callDescriptor.Uri + ".", m_username));
  238. uacCall = new GoogleVoiceUserAgent(m_sipTransport, m_callManager, m_statefulProxyLogEvent, m_username, m_adminMemberId, m_outboundProxySocket);
  239. }
  240. else
  241. {
  242. uacCall = new SIPClientUserAgent(m_sipTransport, m_outboundProxySocket, m_username, m_adminMemberId, m_statefulProxyLogEvent);
  243. }
  244. }
  245. else
  246. {
  247. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Creating B2B call for " + callDescriptor.Uri + ".", m_username));
  248. uacCall = new SIPB2BUserAgent(m_statefulProxyLogEvent, QueueNewCall_External, m_sipTransport, m_username, m_adminMemberId);
  249. }
  250. //ISIPClientUserAgent uacCall = new JingleUserAgent(m_username, m_adminMemberId, m_statefulProxyLogEvent);
  251. lock (m_switchCalls)
  252. {
  253. m_switchCalls.Add(uacCall);
  254. }
  255. uacCall.CallAnswered += UACCallAnswered;
  256. uacCall.CallFailed += UACCallFailed;
  257. uacCall.CallRinging += UACCallProgress;
  258. //uacCall.CallTrying += UACCallTrying;
  259. uacCall.Call(callDescriptor);
  260. }
  261. }
  262. catch (Exception excp)
  263. {
  264. logger.Error("Exception ForkCall StartNewCall. " + excp.Message);
  265. }
  266. }
  267. /*private void UACCallTrying(ISIPClientUserAgent uac, SIPResponse sipResponse)
  268. {
  269. try
  270. {
  271. // Test for the custom multiple redirect response.
  272. if (sipResponse.Status == SIPResponseStatusCodesEnum.MultipleRedirect)
  273. {
  274. ProcessRedirect(uac, sipResponse);
  275. }
  276. }
  277. catch (Exception excp)
  278. {
  279. logger.Error("Exception ForkCall UACCallTrying. " + excp.Message);
  280. }
  281. }*/
  282. /// <summary>
  283. /// This event occurs if it was not possible to initiate a call to the destination specified in the forwarded call. An example
  284. /// would be an unresolvable hostname in the destination URI.
  285. /// </summary>
  286. /// <param name="sipSwitchCall"></param>
  287. /// <param name="errorMessage"></param>
  288. private void UACCallFailed(ISIPClientUserAgent uac, string errorMessage)
  289. {
  290. lock (m_switchCalls)
  291. {
  292. m_switchCalls.Remove(uac);
  293. }
  294. m_lastFailureStatus = SIPResponseStatusCodesEnum.TemporarilyUnavailable;
  295. m_lastFailureReason = errorMessage;
  296. if (m_switchCallTransactions != null && uac.ServerTransaction != null)
  297. {
  298. m_switchCallTransactions.Add(uac.ServerTransaction);
  299. }
  300. uac.CallAnswered -= UACCallAnswered;
  301. uac.CallFailed -= UACCallFailed;
  302. uac.CallRinging -= UACCallProgress;
  303. CallLegCompleted();
  304. }
  305. private void UACCallProgress(ISIPClientUserAgent uac, SIPResponse progressResponse)
  306. {
  307. try
  308. {
  309. if (m_commandCancelled)
  310. {
  311. //logger.Debug("Call " + uac.CallDescriptor.Uri + " should not be in a progress state after a cancel. Cancel again.");
  312. uac.Cancel();
  313. }
  314. else
  315. {
  316. CallProgress(progressResponse.Status, progressResponse.ReasonPhrase, null, progressResponse.Header.ContentType, progressResponse.Body, uac);
  317. }
  318. }
  319. catch (Exception excp)
  320. {
  321. logger.Error("Exception ForkCall UACCallProgress. " + excp);
  322. }
  323. }
  324. private void UACCallAnswered(ISIPClientUserAgent answeredUAC, SIPResponse answeredResponse)
  325. {
  326. try
  327. {
  328. // Remove the current call from the pending list.
  329. lock (m_switchCalls)
  330. {
  331. m_switchCalls.Remove(answeredUAC);
  332. }
  333. if (m_switchCallTransactions != null && answeredUAC.ServerTransaction != null)
  334. {
  335. m_switchCallTransactions.Add(answeredUAC.ServerTransaction);
  336. }
  337. if (answeredResponse != null && answeredResponse.StatusCode >= 200 && answeredResponse.StatusCode <= 299)
  338. {
  339. #region 2xx final response.
  340. if (!m_callAnswered && !m_commandCancelled)
  341. {
  342. // This is the first call we've got an answer on.
  343. m_callAnswered = true;
  344. m_answeredUAC = answeredUAC;
  345. AnsweredSIPResponse = answeredResponse;
  346. SIPDialogueTransferModesEnum uasTransferMode = SIPDialogueTransferModesEnum.Default;
  347. if (m_answeredUAC.CallDescriptor.TransferMode == SIPDialogueTransferModesEnum.NotAllowed)
  348. {
  349. answeredUAC.SIPDialogue.TransferMode = SIPDialogueTransferModesEnum.NotAllowed;
  350. uasTransferMode = SIPDialogueTransferModesEnum.NotAllowed;
  351. }
  352. else if (m_answeredUAC.CallDescriptor.TransferMode == SIPDialogueTransferModesEnum.BlindPlaceCall)
  353. {
  354. answeredUAC.SIPDialogue.TransferMode = SIPDialogueTransferModesEnum.BlindPlaceCall;
  355. uasTransferMode = SIPDialogueTransferModesEnum.BlindPlaceCall;
  356. }
  357. else if (m_answeredUAC.CallDescriptor.TransferMode == SIPDialogueTransferModesEnum.PassThru)
  358. {
  359. answeredUAC.SIPDialogue.TransferMode = SIPDialogueTransferModesEnum.PassThru;
  360. uasTransferMode = SIPDialogueTransferModesEnum.PassThru;
  361. }
  362. /*else if (m_answeredUAC.CallDescriptor.TransferMode == SIPCallTransferModesEnum.Caller)
  363. {
  364. answeredUAC.SIPDialogue.TransferMode = SIPDialogueTransferModesEnum.NotAllowed;
  365. uasTransferMode = SIPDialogueTransferModesEnum.Allowed;
  366. }
  367. else if (m_answeredUAC.CallDescriptor.TransferMode == SIPCallTransferModesEnum.Callee)
  368. {
  369. answeredUAC.SIPDialogue.TransferMode = SIPDialogueTransferModesEnum.Allowed;
  370. uasTransferMode = SIPDialogueTransferModesEnum.NotAllowed;
  371. }
  372. else if (m_answeredUAC.CallDescriptor.TransferMode == SIPCallTransferModesEnum.Both)
  373. {
  374. answeredUAC.SIPDialogue.TransferMode = SIPDialogueTransferModesEnum.Allowed;
  375. uasTransferMode = SIPDialogueTransferModesEnum.Allowed;
  376. }*/
  377. if (CallAnswered != null)
  378. {
  379. logger.Debug("Transfer mode=" + m_answeredUAC.CallDescriptor.TransferMode + ".");
  380. CallAnswered(answeredResponse.Status, answeredResponse.ReasonPhrase, null, null, answeredResponse.Header.ContentType, answeredResponse.Body, answeredUAC.SIPDialogue, uasTransferMode);
  381. }
  382. // Cancel/hangup and other calls on this leg that are still around.
  383. CancelNotRequiredCallLegs(CallCancelCause.NormalClearing);
  384. }
  385. else
  386. {
  387. // Call already answered or cancelled, hangup (send BYE).
  388. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Call leg " + answeredUAC.CallDescriptor.Uri + " answered but call was already answered or cancelled, hanging up.", m_username));
  389. SIPDialogue sipDialogue = new SIPDialogue(answeredUAC.ServerTransaction, m_username, m_adminMemberId);
  390. sipDialogue.Hangup(m_sipTransport, m_outboundProxySocket);
  391. }
  392. #endregion
  393. CallLegCompleted();
  394. }
  395. else if (answeredUAC.SIPDialogue != null)
  396. {
  397. // Google Voice calls create the dialogue without using a SIP response.
  398. if (!m_callAnswered && !m_commandCancelled)
  399. {
  400. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Call leg for Google Voice call to " + answeredUAC.CallDescriptor.Uri + " answered.", m_username));
  401. // This is the first call we've got an answer on.
  402. m_callAnswered = true;
  403. m_answeredUAC = answeredUAC;
  404. if (CallAnswered != null)
  405. {
  406. CallAnswered(SIPResponseStatusCodesEnum.Ok, null, null, null, answeredUAC.SIPDialogue.ContentType, answeredUAC.SIPDialogue.RemoteSDP, answeredUAC.SIPDialogue, SIPDialogueTransferModesEnum.NotAllowed);
  407. }
  408. // Cancel/hangup and other calls on this leg that are still around.
  409. CancelNotRequiredCallLegs(CallCancelCause.NormalClearing);
  410. }
  411. else
  412. {
  413. // Call already answered or cancelled, hangup (send BYE).
  414. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Call leg for Google Voice call to " + answeredUAC.CallDescriptor.Uri + " answered but call was already answered or cancelled, hanging up.", m_username));
  415. answeredUAC.SIPDialogue.Hangup(m_sipTransport, m_outboundProxySocket);
  416. }
  417. }
  418. else if (answeredResponse != null && answeredResponse.StatusCode >= 300 && answeredResponse.StatusCode <= 399)
  419. {
  420. if (answeredUAC.CallDescriptor.RedirectMode == SIPCallRedirectModesEnum.Add)
  421. {
  422. ProcessRedirect(answeredUAC, answeredResponse);
  423. }
  424. else if (answeredUAC.CallDescriptor.RedirectMode == SIPCallRedirectModesEnum.Replace)
  425. {
  426. // In the Replace redirect mode the existing dialplan execution needs to be cancelled and the single redirect call be used to replace it.
  427. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Redirect response rejected as Replace mode not yet implemented.", m_username));
  428. CallLegCompleted();
  429. }
  430. else
  431. {
  432. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Redirect response rejected as not enabled in dial string.", m_username));
  433. CallLegCompleted();
  434. }
  435. }
  436. else if (answeredResponse != null)
  437. {
  438. // This call leg failed, record the failure status and reason.
  439. m_lastFailureStatus = answeredResponse.Status;
  440. m_lastFailureReason = answeredResponse.ReasonPhrase;
  441. if (m_switchCallTransactions != null && answeredUAC.ServerTransaction != null)
  442. {
  443. m_switchCallTransactions.Add(answeredUAC.ServerTransaction);
  444. }
  445. CallLegCompleted();
  446. }
  447. }
  448. catch (Exception excp)
  449. {
  450. logger.Error("Exception ForkCall UACCallAnswered. " + excp);
  451. }
  452. }
  453. private void ProcessRedirect(ISIPClientUserAgent answeredUAC, SIPResponse answeredResponse)
  454. {
  455. try
  456. {
  457. SIPURI redirectURI = answeredResponse.Header.Contact[0].ContactURI;
  458. if (redirectURI == null)
  459. {
  460. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Redirect target could not be determined from redirect response, ignoring.", m_username));
  461. }
  462. else
  463. {
  464. // A redirect response was received. Create a new call leg(s) using the SIP URIs in the contact header of the response.
  465. if (m_dialStringParser != null)
  466. {
  467. // If there is a dial string parser available it will be used to generate a list of call destination from the redirect URI.
  468. SIPCallDescriptor redirectCallDescriptor = answeredUAC.CallDescriptor.CopyOf();
  469. Queue<List<SIPCallDescriptor>> redirectQueue = m_dialStringParser.ParseDialString(DialPlanContextsEnum.Script, null, redirectURI.ToString(), redirectCallDescriptor.CustomHeaders,
  470. redirectCallDescriptor.ContentType, redirectCallDescriptor.Content, null, redirectCallDescriptor.FromDisplayName, redirectCallDescriptor.FromURIUsername, redirectCallDescriptor.FromURIHost, null);
  471. if (redirectQueue != null && redirectQueue.Count > 0)
  472. {
  473. // Only the first list in the queue is used (and there should only be a single list since it's generated from a redirect SIP URI and not
  474. // a full dial string).
  475. List<SIPCallDescriptor> callDescriptors = redirectQueue.Dequeue();
  476. for (int index = 0; index < callDescriptors.Count; index++)
  477. {
  478. callDescriptors[index].MangleIPAddress = redirectCallDescriptor.MangleIPAddress;
  479. callDescriptors[index].MangleResponseSDP = redirectCallDescriptor.MangleResponseSDP;
  480. callDescriptors[index].TransferMode = redirectCallDescriptor.TransferMode;
  481. }
  482. Start(callDescriptors);
  483. }
  484. else
  485. {
  486. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "A redirect response to " + redirectURI.ToString() + " did not generate any new call leg destinations.", m_username));
  487. }
  488. }
  489. else
  490. {
  491. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Redirect response to " + redirectURI.ToString() + " accepted.", m_username));
  492. SIPCallDescriptor redirectCallDescriptor = answeredUAC.CallDescriptor.CopyOf();
  493. redirectCallDescriptor.Uri = redirectURI.ToString();
  494. StartNewCallAsync(redirectCallDescriptor);
  495. }
  496. }
  497. }
  498. catch (Exception excp)
  499. {
  500. logger.Error("Exception ForkCall ProcessRedirect. " + excp.Message);
  501. }
  502. }
  503. public void CancelNotRequiredCallLegs(CallCancelCause cancelCause)
  504. {
  505. try
  506. {
  507. m_commandCancelled = true;
  508. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Cancelling all call legs for ForkCall app.", m_username));
  509. // Cancel all forwarded call legs.
  510. if (m_switchCalls.Count > 0)
  511. {
  512. ISIPClientUserAgent[] inProgressCalls = (from ua in m_switchCalls where !ua.IsUACAnswered select ua).ToArray();
  513. for (int index = 0; index < inProgressCalls.Length; index++)
  514. {
  515. ISIPClientUserAgent uac = inProgressCalls[index];
  516. uac.Cancel();
  517. }
  518. }
  519. // Signal any delayed calls that they are no longer required.
  520. foreach (SIPCallDescriptor callDescriptor in m_delayedCalls)
  521. {
  522. callDescriptor.DelayMRE.Set();
  523. }
  524. CallLegCompleted();
  525. }
  526. catch (Exception excp)
  527. {
  528. logger.Error("Exception ForkCall CancelAllCallLegs. " + excp);
  529. }
  530. }
  531. /// <summary>
  532. /// Fired after each call leg forward attempt is completed.
  533. /// </summary>
  534. private void CallLegCompleted()
  535. {
  536. try
  537. {
  538. if (!m_callAnswered && !m_commandCancelled)
  539. {
  540. if (m_switchCalls.Count > 0 || m_delayedCalls.Count > 0)
  541. {
  542. // There are still calls on this leg in progress.
  543. // If there are no current calls then start the next delayed one.
  544. if (m_switchCalls.Count == 0)
  545. {
  546. SIPCallDescriptor nextCall = null;
  547. lock (m_delayedCalls)
  548. {
  549. foreach (SIPCallDescriptor call in m_delayedCalls)
  550. {
  551. if (nextCall == null || nextCall.DelaySeconds > call.DelaySeconds)
  552. {
  553. nextCall = call;
  554. }
  555. }
  556. }
  557. if (nextCall != null)
  558. {
  559. nextCall.DelayMRE.Set();
  560. }
  561. }
  562. }
  563. else if (m_priorityCallsQueue.Count != 0 && !m_callAnswered)
  564. {
  565. List<SIPCallDescriptor> nextPrioritycalls = m_priorityCallsQueue.Dequeue();
  566. Start(nextPrioritycalls);
  567. }
  568. else if (CallFailed != null)
  569. {
  570. // No more call legs to attempt, or call has already been answered or cancelled.
  571. if (m_lastFailureStatus != SIPResponseStatusCodesEnum.None)
  572. {
  573. CallFailed(m_lastFailureStatus, m_lastFailureReason, null);
  574. }
  575. else
  576. {
  577. CallFailed(SIPResponseStatusCodesEnum.TemporarilyUnavailable, "All forwards failed.", null);
  578. }
  579. }
  580. }
  581. }
  582. catch (Exception excp)
  583. {
  584. logger.Error("Exception CallLegCompleted. " + excp);
  585. }
  586. }
  587. private void FireProxyLogEvent(SIPMonitorEvent monitorEvent)
  588. {
  589. try
  590. {
  591. if (m_statefulProxyLogEvent != null)
  592. {
  593. m_statefulProxyLogEvent(monitorEvent);
  594. }
  595. }
  596. catch (Exception excp)
  597. {
  598. logger.Error("Exception FireProxyLogEvent ForkCall. " + excp.Message);
  599. }
  600. }
  601. }
  602. }