/sipsorcery-core/SIPSorcery.AppServer.DialPlan/DialPlanFacades/DialPlanScriptFacade.cs
C# | 1951 lines | 1477 code | 176 blank | 298 comment | 219 complexity | b3bac3cb7511a95d249b088063d7731f MD5 | raw file
Possible License(s): CC-BY-SA-3.0
Large files files are truncated, but you can click here to view the full file
- // ============================================================================
- // FileName: DialPlanScriptFacade.cs
- //
- // Description:
- // Dial plan script facade or helper methods for dial plan scripts.
- //
- // Author(s):
- // Aaron Clauson
- //
- // History:
- // 16 Sep 2008 Aaron Clauson Created (extracted from SIPDialPlan).
- //
- // License:
- // This software is licensed under the BSD License http://www.opensource.org/licenses/bsd-license.php
- //
- // Copyright (c) 2010 Aaron Clauson (aaron@sipsorcery.com), SIP Sorcery Pty Ltd
- // All rights reserved.
- //
- // Redistribution and use in source and binary forms, with or without modification, are permitted provided that
- // the following conditions are met:
- //
- // Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
- // Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
- // disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of Blue Face Ltd.
- // nor the names of its contributors may be used to endorse or promote products derived from this software without specific
- // prior written permission.
- //
- // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
- // BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- // IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
- // OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
- // OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- // POSSIBILITY OF SUCH DAMAGE.
- // ============================================================================
-
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Collections.Specialized;
- using System.Data;
- using System.IO;
- using System.Linq;
- using System.Net;
- using System.Net.Sockets;
- using System.Security;
- using System.Security.Principal;
- using System.Text;
- using System.Text.RegularExpressions;
- using System.Threading;
- using System.Web;
- using SIPSorcery.Net;
- using SIPSorcery.Persistence;
- using SIPSorcery.SIP;
- using SIPSorcery.SIP.App;
- using SIPSorcery.Sys;
- using System.Transactions;
- using Heijden.DNS;
- using log4net;
- using agsXMPP;
- using agsXMPP.protocol;
- using agsXMPP.protocol.client;
- using GaDotNet.Common.Data;
- using GaDotNet.Common.Helpers;
- using GaDotNet.Common;
-
- #if UNITTEST
- using NUnit.Framework;
- #endif
-
- namespace SIPSorcery.AppServer.DialPlan
- {
- /// <summary>
- /// Helper functions for use in dial plan scripts.
- /// </summary>
- public class DialPlanScriptFacade
- {
- private const int DEFAULT_CREATECALL_RINGTIME = 60;
- private const int ENUM_LOOKUP_TIMEOUT = 5; // Default timeout in seconds for ENUM lookups.
- private const string DEFAULT_LOCAL_DOMAIN = "local";
- private const int MAX_BYTES_WEB_GET = 8192; // The maximum number of bytes that will be read from the response stream in the WebGet application.
- private const string EMAIL_FROM_ADDRESS = "dialplan@sipsorcery.com"; // The from address that will be set for emails sent from the dialplan.
- private const int ALLOWED_ADDRESSES_PER_EMAIL = 5; // The maximum number of addresses that can be used in an email.
- private const int ALLOWED_EMAILS_PER_EXECUTION = 3; // The maximum number of emails that can be sent pre dialplan execution.
- private const int MAX_EMAIL_SUBJECT_LENGTH = 256;
- private const int MAX_EMAIL_BODY_LENGTH = 2048;
- private const string USERDATA_DBTYPE_KEY = "UserDataDBType";
- private const string USERDATA_DBCONNSTR_KEY = "UserDataDBConnStr";
- private const int MAX_DATA_ENTRIES_PER_USER = 100;
- private const int MAX_CALLS_ALLOWED = 20; // The maximum number of outgoing call requests that will be allowed per dialplan execution.
- private const int MAX_CALLBACKS_ALLOWED = 3; // The maximum number of callback method calls that will be alowed per dialplan instance.
- private const int WEBGET_MAXIMUM_TIMEOUT = 300; // The maximum number of seconds a web get can be set to wait for a response.
- private const int DEFAULT_GOOGLEVOICE_PHONETYPE = 2;
- private const int GTALK_DEFAULT_CONNECTION_TIMEOUT = 5000;
- private const int GTALK_MAX_CONNECTION_TIMEOUT = 30000;
- private const string GOOGLE_ANALYTICS_KEY = "GoogleAnalyticsAccountCode"; // If this key does not exist in App.Config there's no point sending Google Analytics requests.
-
- private static int m_maxRingTime = SIPTimings.MAX_RING_TIME;
-
- private static readonly StorageTypes m_userDataDBType = StorageTypes.Unknown;
- private static readonly string m_userDataDBConnStr;
- private static bool m_sendAnalytics = true;
-
- private static ILog logger = AppState.logger;
- private SIPMonitorLogDelegate m_dialPlanLogDelegate;
-
- private SIPTransport m_sipTransport;
- private DialPlanExecutingScript m_executingScript;
- private List<SIPProvider> m_sipProviders;
- private DialogueBridgeCreatedDelegate CreateBridge_External;
-
- private GetCanonicalDomainDelegate m_getCanonicalDomainDelegate;
- private SIPRequest m_sipRequest; // This is a copy of the SIP request from m_clientTransaction.
- private ForkCall m_currentCall;
- private SIPEndPoint m_outboundProxySocket; // If this app forwards calls via an outbound proxy this value will be set.
- private List<string> m_customSIPHeaders = new List<string>(); // Allows a dialplan user to add or customise SIP headers for forwarded requests.
- private string m_customContent; // If set will be used by the Dial command as the INVITE body on forwarded requests.
- private string m_customContentType;
- private string m_customFromName;
- private string m_customFromUser;
- private string m_customFromHost;
- private SIPCallDirection m_callDirection;
- private DialStringParser m_dialStringParser;
- private bool m_clientCallCancelled;
- private ManualResetEvent m_waitForCallCompleted;
-
- // Deprecated, use LastFailureReason.
- public string LastFailureMessage
- {
- get { return LastFailureReason; }
- }
-
- // The error message from the first call leg on the final dial attempt used when the call fails to provide a reason.
- public string LastFailureReason
- {
- get { return m_executingScript.LastFailureReason; }
- set { m_executingScript.LastFailureReason = value; }
- }
- public SIPResponseStatusCodesEnum LastFailureStatus
- {
- get { return m_executingScript.LastFailureStatus; }
- set { m_executingScript.LastFailureStatus = value; }
- }
- private int m_emailCount = 0; // Keeps count of the emails that have been sent during this dialpan execution.
- private int m_callInitialisationCount = 0; // Keeps count of the number of call initialisations that have been attempted by a dialplan execution.
- private int m_callbackRequests = 0; // Keeps count of the number of call back requests that have been attempted by a dialplan execution.
- private IDbConnection m_userDataDBConnection;
- private IDbTransaction m_userDataDBTransaction;
- private Dictionary<string, XmppClientConnection> m_gtalkConnections = new Dictionary<string, XmppClientConnection>();
-
- private SIPAssetPersistor<SIPAccount> m_sipAccountPersistor;
- private SIPAssetPersistor<SIPDialPlan> m_sipDialPlanPersistor;
- private SIPAssetPersistor<SIPDialogueAsset> m_sipDialoguePersistor;
- private SIPAssetGetListDelegate<SIPRegistrarBinding> GetSIPAccountBindings_External; // This event must be wired up to an external function in order to be able to lookup bindings that have been registered for a SIP account.
- private ISIPCallManager m_callManager;
- private bool m_autoCleanup = true; // Set to false if the Ruby dialplan wants to take care of handling the cleanup from a rescue or ensure block.
- private bool m_hasBeenCleanedUp;
-
- private DialPlanContext m_dialPlanContext;
- public DialPlanContext DialPlanContext
- {
- get { return m_dialPlanContext; }
- }
-
- private string m_username;
- public string Username
- {
- get { return m_username; }
- }
- private string m_adminMemberId;
- public bool Out
- {
- get { return m_callDirection == SIPCallDirection.Out; }
- }
- public bool In
- {
- get { return m_callDirection == SIPCallDirection.In; }
- }
- public List<SIPTransaction> LastDialled;
- public bool Trace
- {
- get { return m_dialPlanContext.SendTrace; }
- set { m_dialPlanContext.SendTrace = value; }
- }
-
- public static IPAddress PublicIPAddress; // If the app server is behind a NAT then it can set this address to be used in mangled SDP.
-
- static DialPlanScriptFacade()
- {
- try
- {
- m_userDataDBType = (AppState.GetConfigSetting(USERDATA_DBTYPE_KEY) != null) ? StorageTypesConverter.GetStorageType(AppState.GetConfigSetting(USERDATA_DBTYPE_KEY)) : StorageTypes.Unknown;
- m_userDataDBConnStr = AppState.GetConfigSetting(USERDATA_DBCONNSTR_KEY);
- m_sendAnalytics = !AppState.GetConfigSetting(GOOGLE_ANALYTICS_KEY).IsNullOrBlank();
- }
- catch (Exception excp)
- {
- logger.Error("Exception DialPlanScriptHelper (static ctor). " + excp.Message);
- }
- }
-
- public DialPlanScriptFacade(
- SIPTransport sipTransport,
- DialPlanExecutingScript executingScript,
- SIPMonitorLogDelegate logDelegate,
- DialogueBridgeCreatedDelegate createBridge,
- SIPRequest sipRequest,
- SIPCallDirection callDirection,
- DialPlanContext dialPlanContext,
- GetCanonicalDomainDelegate getCanonicalDomain,
- ISIPCallManager callManager,
- SIPAssetPersistor<SIPAccount> sipAccountPersistor,
- SIPAssetPersistor<SIPDialPlan> sipDialPlanPersistor,
- SIPAssetPersistor<SIPDialogueAsset> sipDialoguePersistor,
- SIPAssetGetListDelegate<SIPRegistrarBinding> getSIPAccountBindings,
- SIPEndPoint outboundProxySocket
- )
- {
- m_sipTransport = sipTransport;
- m_executingScript = executingScript;
- m_dialPlanLogDelegate = logDelegate;
- CreateBridge_External = createBridge;
- m_sipRequest = sipRequest;
- m_callDirection = callDirection;
- m_dialPlanContext = dialPlanContext;
- m_getCanonicalDomainDelegate = getCanonicalDomain;
- m_callManager = callManager;
- m_sipAccountPersistor = sipAccountPersistor;
- m_sipDialPlanPersistor = sipDialPlanPersistor;
- m_sipDialoguePersistor = sipDialoguePersistor;
- GetSIPAccountBindings_External = getSIPAccountBindings;
- m_outboundProxySocket = outboundProxySocket;
-
- m_executingScript.Cleanup = CleanupDialPlanScript;
-
- if (m_dialPlanContext != null)
- {
- m_username = dialPlanContext.Owner;
- m_adminMemberId = dialPlanContext.AdminMemberId;
- m_sipProviders = dialPlanContext.SIPProviders;
-
- m_dialPlanContext.TraceLog.AppendLine("DialPlan=> Dialplan trace commenced at " + DateTime.Now.ToString("dd MMM yyyy HH:mm:ss:fff") + ".");
- m_dialPlanContext.CallCancelledByClient += ClientCallTerminated;
-
- SIPAssetGetDelegate<SIPAccount> getSIPAccount = null;
- if (m_sipAccountPersistor != null)
- {
- getSIPAccount = m_sipAccountPersistor.Get;
- }
- m_dialStringParser = new DialStringParser(m_sipTransport, m_dialPlanContext.Owner, m_dialPlanContext.SIPAccount, m_sipProviders, getSIPAccount, GetSIPAccountBindings_External, m_getCanonicalDomainDelegate, logDelegate, m_dialPlanContext.SIPDialPlan.DialPlanName);
- }
- }
-
- public void TurnOffAutoCleanup()
- {
- m_autoCleanup = false;
- }
-
- public void Cleanup()
- {
- m_autoCleanup = true;
- CleanupDialPlanScript();
- }
-
- /// <summary>
- /// A function that gets attached to the executing thread object and that will be called when the dialplan script is complete and
- /// immediately prior to a possible thread abortion.
- /// </summary>
- private void CleanupDialPlanScript()
- {
- try
- {
- if (m_hasBeenCleanedUp)
- {
- Log("Dialplan cleanup has already been run, ignoring subsequent cleanup call for " + Username + ".");
- }
- else if (m_autoCleanup)
- {
- m_hasBeenCleanedUp = true;
- Log("Dialplan cleanup for " + Username + ".");
-
- if (m_userDataDBConnection != null)
- {
- Log("Closing user database connection for " + Username + ".");
- m_userDataDBConnection.Close();
- }
-
- if (m_gtalkConnections.Count > 0)
- {
- foreach (KeyValuePair<string, XmppClientConnection> connection in m_gtalkConnections)
- {
- Log("Closing gTalk connection for " + connection.Key + ".");
- connection.Value.Close();
- }
- }
- }
- }
- catch (Exception excp)
- {
- logger.Error("Exception DialPlanScriptFacade Cleanup. " + excp.Message);
- Log("Exception DialPlanScriptFacade Cleanup. " + excp.Message);
- }
- }
-
- public string WhoAmI()
- {
- return WindowsIdentity.GetCurrent().Name;
- }
-
- /// <remarks>
- /// This method will be called on the thread that owns the dialplan context object so it's critical that Thread abort
- /// is not called in it or from it.
- /// </remarks>
- /// <param name="cancelCause"></param>
- private void ClientCallTerminated(CallCancelCause cancelCause)
- {
- try
- {
- m_clientCallCancelled = true;
-
- Log("Dialplan call was terminated by client side due to " + cancelCause + ".");
-
- if (m_currentCall != null)
- {
- m_currentCall.CancelNotRequiredCallLegs(cancelCause);
- }
-
- if (m_waitForCallCompleted != null)
- {
- m_waitForCallCompleted.Set();
- }
- else
- {
- m_executingScript.StopExecution();
- }
- }
- catch (Exception excp)
- {
- logger.Error("Exception ClientCallTerminated. " + excp.Message);
- }
- }
-
- /// <summary>
- /// Attempts to dial a series of forwards and bridges the first one that connects with the client call.
- /// </summary>
- /// <param name="data">The dial string containing the list of call legs to attempt to forward the call to.</param>
- /// <returns>A code that best represents how the dial command ended.</returns>
- public DialPlanAppResult Dial(string data)
- {
- return Dial(data, m_maxRingTime);
- }
-
- /// <summary>
- /// Attempts to dial a series of forwards and bridges the first one that connects with the client call.
- /// </summary>
- /// <param name="data">The dial string containing the list of call legs to attempt to forward the call to.</param>
- /// <param name="ringTimeout">The period in seconds to perservere with the dial command attempt without a final response before giving up.</param>
- /// <returns>A code that best represents how the dial command ended.</returns>
- public DialPlanAppResult Dial(string data, int ringTimeout)
- {
- return Dial(data, ringTimeout, 0);
- }
-
- /// <summary>
- /// Attempts to dial a series of forwards and bridges the first one that connects with the client call.
- /// </summary>
- /// <param name="data">The dial string containing the list of call legs to attempt to forward the call to.</param>
- /// /// <param name="answeredCallLimit">If greater than 0 this specifies the period in seconds an answered call will be hungup after.</param>
- /// <param name="ringTimeout">The period in seconds to perservere with the dial command attempt without a final response before giving up.</param>
- /// <returns>A code that best represents how the dial command ended.</returns>
- public DialPlanAppResult Dial(string data, int ringTimeout, int answeredCallLimit)
- {
- return Dial(data, ringTimeout, answeredCallLimit, m_sipRequest, null);
- }
-
- //public DialPlanAppResult Dial(string data, CRMHeaders contact)
- //{
- // return Dial(data, m_maxRingTime, 0, m_sipRequest, null);
- //}
-
- public DialPlanAppResult Dial(string data, int ringTimeout, int answeredCallLimit, CRMHeaders contact)
- {
- return Dial(data, ringTimeout, answeredCallLimit, m_sipRequest, contact);
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="data"></param>
- /// <param name="ringTimeout"></param>
- /// <param name="answeredCallLimit"></param>
- /// <param name="redirectMode"></param>
- /// <param name="clientTransaction"></param>
- /// <param name="keepScriptAlive">If false will let the dial plan engine know the script has finished and the call is answered. For applications
- /// like Callback which need to have two calls answered it will be true.</param>
- /// <returns></returns>
- private DialPlanAppResult Dial(
- string data,
- int ringTimeout,
- int answeredCallLimit,
- SIPRequest clientRequest,
- CRMHeaders contact)
- {
- if (m_dialPlanContext.IsAnswered)
- {
- Log("The call has already been answered the Dial command was not processed.");
- return DialPlanAppResult.AlreadyAnswered;
- }
- else if (data.IsNullOrBlank())
- {
- Log("The dial string cannot be empty when calling Dial.");
- return DialPlanAppResult.Error;
- }
- else if (m_callInitialisationCount > MAX_CALLS_ALLOWED)
- {
- Log("You have exceeded the maximum allowed calls for a dialplan execution.");
- return DialPlanAppResult.Error;
- }
- else
- {
- Log("Commencing Dial with: " + data + ".");
-
- DialPlanAppResult result = DialPlanAppResult.Unknown;
- m_waitForCallCompleted = new ManualResetEvent(false);
-
- SIPResponseStatusCodesEnum answeredStatus = SIPResponseStatusCodesEnum.None;
- string answeredReason = null;
- string answeredContentType = null;
- string answeredBody = null;
- SIPDialogue answeredDialogue = null;
- SIPDialogueTransferModesEnum uasTransferMode = SIPDialogueTransferModesEnum.Default;
- int numberLegs = 0;
-
- m_currentCall = new ForkCall(m_sipTransport, FireProxyLogEvent, m_callManager.QueueNewCall, m_dialStringParser, Username, m_adminMemberId, m_outboundProxySocket, m_callManager, out LastDialled);
- m_currentCall.CallProgress += m_dialPlanContext.CallProgress;
- m_currentCall.CallFailed += (status, reason, headers) =>
- {
- LastFailureStatus = status;
- LastFailureReason = reason;
- result = DialPlanAppResult.Failed;
- m_waitForCallCompleted.Set();
- };
- m_currentCall.CallAnswered += (status, reason, toTag, headers, contentType, body, dialogue, transferMode) =>
- {
- answeredStatus = status;
- answeredReason = reason;
- answeredContentType = contentType;
- answeredBody = body;
- answeredDialogue = dialogue;
- uasTransferMode = transferMode;
- result = DialPlanAppResult.Answered;
- m_waitForCallCompleted.Set();
- };
-
- try
- {
- Queue<List<SIPCallDescriptor>> callsQueue = m_dialStringParser.ParseDialString(
- DialPlanContextsEnum.Script,
- clientRequest,
- data,
- m_customSIPHeaders,
- m_customContentType,
- m_customContent,
- m_dialPlanContext.CallersNetworkId,
- m_customFromName,
- m_customFromUser,
- m_customFromHost,
- contact);
-
- List<SIPCallDescriptor>[] callListArray = callsQueue.ToArray();
- callsQueue.ToList().ForEach((list) => numberLegs += list.Count);
-
- if (numberLegs == 0)
- {
- Log("The dial string did not result in any call legs.");
- return DialPlanAppResult.Error;
- }
- else
- {
- m_callInitialisationCount += numberLegs;
- if (m_callInitialisationCount > MAX_CALLS_ALLOWED)
- {
- Log("You have exceeded the maximum allowed calls for a dialplan execution.");
- return DialPlanAppResult.Error;
- }
- }
-
- m_currentCall.Start(callsQueue);
-
- // Wait for an answer.
- if (ringTimeout <= 0 || ringTimeout * 1000 > m_maxRingTime)
- {
- ringTimeout = m_maxRingTime;
- }
- else
- {
- ringTimeout = ringTimeout * 1000;
- }
- ExtendScriptTimeout(ringTimeout/1000 + DEFAULT_CREATECALL_RINGTIME);
- DateTime startTime = DateTime.Now;
-
- if (m_waitForCallCompleted.WaitOne(ringTimeout, false))
- {
- if (!m_clientCallCancelled)
- {
- if (result == DialPlanAppResult.Answered)
- {
- // The call limit duration is only used if there hasn't already been a per leg duration set on the call.
- if (answeredCallLimit > 0 && answeredDialogue.CallDurationLimit == 0)
- {
- answeredDialogue.CallDurationLimit = answeredCallLimit;
- }
-
- m_dialPlanContext.CallAnswered(answeredStatus, answeredReason, null, null, answeredContentType, answeredBody, answeredDialogue, uasTransferMode);
-
- // Dial plan script stops once there is an answered call to bridge to or the client call is cancelled.
- Log("Dial command was successfully answered in " + DateTime.Now.Subtract(startTime).TotalSeconds.ToString("0.00") + "s.");
-
- // Do some Google Analytics call tracking.
- if (answeredDialogue.RemoteUserField != null)
- {
- SendGoogleAnalyticsEvent("Call", "Answered", answeredDialogue.RemoteUserField.URI.Host, 1);
- }
-
- m_executingScript.StopExecution();
- }
- }
- }
- else
- {
- if (!m_clientCallCancelled)
- {
- // Call timed out.
- m_currentCall.CancelNotRequiredCallLegs(CallCancelCause.TimedOut);
- result = DialPlanAppResult.TimedOut;
- }
- }
-
- if (m_clientCallCancelled)
- {
- Log("Dial command was halted by cancellation of client call after " + DateTime.Now.Subtract(startTime).TotalSeconds.ToString("#.00") + "s.");
- m_executingScript.StopExecution();
- }
-
- return result;
- }
- catch (ThreadAbortException)
- {
- return DialPlanAppResult.Unknown;
- }
- catch (Exception excp)
- {
- logger.Error("Exception DialPlanScriptHelper Dial. " + excp.Message);
- return DialPlanAppResult.Error;
- }
- }
- }
-
- /// <summary>
- /// Logs a message with the proxy. Typically this records the message in the database and also prints it out
- /// on the proxy monitor telnet console.
- /// </summary>
- /// <param name="message"></param>
- public void Log(string message)
- {
- FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, message, Username));
- }
-
- /// <summary>
- /// See Callback method below.
- /// </summary>
- public void Callback(string dest1, string dest2)
- {
- Callback(dest1, dest2, 0);
- }
-
- public void Callback(string dest1, string dest2, int delaySeconds)
- {
- m_callbackRequests++;
- if (m_callbackRequests > MAX_CALLBACKS_ALLOWED)
- {
- Log("You have exceeded the maximum allowed callbacks for a dialplan execution.");
- }
- else
- {
- CallbackApp callbackApp = new CallbackApp(m_sipTransport, m_callManager, m_dialStringParser, FireProxyLogEvent, m_username, m_adminMemberId, m_outboundProxySocket);
- ThreadPool.QueueUserWorkItem(delegate { callbackApp.Callback(dest1, dest2, delaySeconds); });
- }
- }
-
- public void Respond(int statusCode, string reason)
- {
- Respond(statusCode, reason, null);
- }
-
- /// <summary>
- /// Sends a SIP response to the client call. If a final response is sent then the client call will hang up.
- /// </summary>
- /// <param name="statusCode"></param>
- /// <param name="reason"></param>
- /// <param name="customerHeaders">Optional list of pipe '|' delimited custom headers.</param>
- public void Respond(int statusCode, string reason, string customHeaders)
- {
- try
- {
- if (m_dialPlanContext.IsAnswered)
- {
- Log("The call has already been answered the Respond command was not processed.");
- }
- else if (statusCode >= 200 && statusCode < 300)
- {
- Log("Respond cannot be used for 2xx responses.");
- }
- else
- {
- string[] customHeadersList = null;
- if (!customHeaders.IsNullOrBlank())
- {
- customHeadersList = customHeaders.Split('|');
- }
-
- SIPResponseStatusCodesEnum status = SIPResponseStatusCodes.GetStatusTypeForCode(statusCode);
- if (statusCode >= 300)
- {
- m_dialPlanContext.CallFailed(status, reason, customHeadersList);
- m_executingScript.StopExecution();
- }
- else if (statusCode < 200)
- {
- m_dialPlanContext.CallProgress(status, reason, customHeadersList, null, null, null);
- }
- }
- }
- catch (ThreadAbortException) { }
- catch (Exception excp)
- {
- Log("Exception Respond. " + excp.Message);
- }
- }
-
- /// <summary>
- /// Trys an ENUM lookup on the specified number.
- /// </summary>
- /// <param name="number"></param>
- /// <returns></returns>
- public string ENUMLookup(string number)
- {
- try
- {
- string e164DottedNumber = FormatForENUMLookup(number);
-
- if (number == null)
- {
- logger.Warn("The ENUMLookup number format was not recognised needs to be number.domain.");
- return null;
- }
- else
- {
- logger.Debug("Starting ENUM lookup for " + e164DottedNumber + ".");
-
- DNSResponse enumResponse = DNSManager.Lookup(e164DottedNumber, DNSQType.NAPTR, ENUM_LOOKUP_TIMEOUT, null, false, false);
- if (enumResponse.Answers != null && enumResponse.RecordNAPTR != null)
- {
- foreach (RecordNAPTR naptr in enumResponse.RecordNAPTR)
- {
- logger.Debug("NAPTR result=" + naptr.ToString() + " (ttl=" + naptr.RR.TTL + ").");
- }
-
- string enumURI = ApplyENUMRules(number, enumResponse.RecordNAPTR);
-
- if (enumURI != null)
- {
- logger.Debug("ENUM URI found for " + number + "=" + enumURI);
- return enumURI;
- }
- else
- {
- return null;
- }
- }
- else
- {
- logger.Debug("No NAPTR records found for " + number + ".");
- return null;
- }
- }
- }
- catch (Exception excp)
- {
- logger.Error("Exception ENUMLookup. " + excp.Message);
- return null;
- }
- }
-
- /// <summary>
- /// Checks whether the dialplan owner's default SIP account is online (has any current bindings).
- /// </summary>
- public bool IsAvailable()
- {
- return IsAvailable(Username, DEFAULT_LOCAL_DOMAIN);
- }
-
- /// <summary>
- /// Checks whether SIP account belonging to the default server domain is online (has any current bindings).
- /// </summary>
- public bool IsAvailable(string username)
- {
- return IsAvailable(username, DEFAULT_LOCAL_DOMAIN);
- }
-
- /// <summary>
- /// Checks whether the specified SIP account is online (has any current bindings).
- /// </summary>
- public bool IsAvailable(string username, string domain)
- {
- try
- {
- string canonicalDomain = m_getCanonicalDomainDelegate(domain, false);
- if (canonicalDomain.IsNullOrBlank())
- {
- FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "The " + domain + " is not a serviced domain.", Username));
- return false;
- }
- else
- {
- SIPAccount sipAccount = m_sipAccountPersistor.Get(s => s.SIPUsername == username && s.SIPDomain == canonicalDomain);
- if (sipAccount == null)
- {
- FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "No sip account exists in IsAvailable for " + username + "@" + canonicalDomain + ".", Username));
- return false;
- }
- else
- {
- SIPRegistrarBinding[] bindings = GetBindings(username, canonicalDomain);
- return (bindings != null && bindings.Length > 0);
- }
- }
- }
- catch (Exception excp)
- {
- Log("Exception IsAvailable. " + excp.Message);
- return false;
- }
- }
-
- /// <summary>
- /// Checks whether the specified SIP account and default domain belongs to the dialplan owner.
- /// </summary>
- public bool IsMine(string username)
- {
- return IsAvailable(username, DEFAULT_LOCAL_DOMAIN);
- }
-
- /// <summary>
- /// Checks whether the specified SIP account belongs to the dialplan owner.
- /// </summary>
- public bool IsMine(string username, string domain)
- {
- try
- {
- string canonicalDomain = m_getCanonicalDomainDelegate(domain, false);
- if (canonicalDomain.IsNullOrBlank())
- {
- FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "The " + domain + " is not a serviced domain.", Username));
- return false;
- }
- else
- {
- SIPAccount sipAccount = m_sipAccountPersistor.Get(s => s.SIPUsername == username && s.SIPDomain == canonicalDomain);
- if (sipAccount == null)
- {
- FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "No sip account exists in IsMine for " + username + "@" + canonicalDomain + ".", Username));
- return false;
- }
- else
- {
- return (sipAccount.Owner == Username);
- }
- }
- }
- catch (Exception excp)
- {
- Log("Exception IsMine. " + excp.Message);
- return false;
- }
- }
-
- /// <summary>
- /// Used to check for the existence of a SIP account in the default domain.
- /// </summary>
- /// <param name="username">The SIP account username to check for.</param>
- /// <returns>Returns true if the SIP account exists, false otherwise.</returns>
- public bool DoesSIPAccountExist(string username)
- {
- return DoesSIPAccountExist(username, DEFAULT_LOCAL_DOMAIN);
- }
-
- /// <summary>
- /// Used to check for the existence of a SIP account in the specified domain.
- /// </summary>
- /// <param name="username">The SIP account username to check for.</param>
- /// <param name="domain">The SIP domain to check for the account in.</param>
- /// <returns>Returns true if the SIP account exists, false otherwise.</returns>
- public bool DoesSIPAccountExist(string username, string domain)
- {
- string canonicalDomain = m_getCanonicalDomainDelegate(domain, false);
- if (!canonicalDomain.IsNullOrBlank())
- {
- return (m_sipAccountPersistor.Count(s => s.SIPUsername == username && s.SIPDomain == canonicalDomain) > 0);
- }
- else
- {
- return false;
- }
- }
-
- /// <summary>
- /// Gets an array of the registered contacts for the dialplan owner's SIP account.
- /// </summary>
- public SIPRegistrarBinding[] GetBindings()
- {
- string canonicalDomain = m_getCanonicalDomainDelegate(DEFAULT_LOCAL_DOMAIN, true);
- return GetBindings(Username, canonicalDomain);
- }
-
- /// <summary>
- /// Gets an array of the registered contacts for the specified SIP account. Only the owner of the SIP account
- /// will be allowed to retrieve a list of bindings for it.
- /// </summary>
- public SIPRegistrarBinding[] GetBindings(string username, string domain)
- {
- try
- {
- string canonicalDomain = m_getCanonicalDomainDelegate(domain, false);
- if (canonicalDomain.IsNullOrBlank())
- {
- FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "The " + domain + " is not a serviced domain.", Username));
- return null;
- }
- else
- {
- SIPAccount sipAccount = m_sipAccountPersistor.Get(s => s.SIPUsername == username && s.SIPDomain == domain);
- if (sipAccount == null)
- {
- FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "No sip account exists in GetBindings for " + username + "@" + domain + ".", Username));
- return null;
- }
- else if (sipAccount.Owner != m_username)
- {
- FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "You are not authorised to call GetBindings for " + username + "@" + domain + ".", Username));
- return null;
- }
- else
- {
- List<SIPRegistrarBinding> bindings = GetSIPAccountBindings_External(s => s.SIPAccountId == sipAccount.Id, null, 0, Int32.MaxValue);
-
- if (bindings != null)
- {
- return bindings.ToArray();
- }
- else
- {
- return null;
- }
- }
- }
- }
- catch (Exception excp)
- {
- Log("Exception GetBindings. " + excp);
- return null;
- }
- }
-
- /// <summary>
- /// Allows a hostname to be tested to determine if it is a sipsorcery serviced domain and if it is the canonical domain
- /// will be returned.
- /// </summary>
- /// <param name="host">The hostname to attempt to retrieve the canonical domain for.</param>
- /// <returns>If the host is a sipsorcery serviced domain the canonical domain for the host will be returned, otherwise null.</returns>
- public string GetCanonicalDomain(string host)
- {
- if (host.IsNullOrBlank())
- {
- return null;
- }
- else
- {
- return m_getCanonicalDomainDelegate(host, false);
- }
- }
-
- /// <summary>
- /// Adds a name value pair to the custom SIP headers list. The custom headers will be added to any forwarded call requests.
- /// </summary>
- /// <param name="headerName">The name of the SIP header to add.</param>
- /// <param name="headerValue">The value of the SIP header to add.</param>
- public void SetCustomSIPHeader(string headerName, string headerValue)
- {
- if (headerName.IsNullOrBlank())
- {
- Log("The name of the header to set was empty, the header was not added.");
- }
- else if (Regex.Match(headerName.Trim(), @"^(Via|To|From|Contact|CSeq|Call-ID|Max-Forwards|Content-Length)$", RegexOptions.IgnoreCase).Success)
- {
- Log("The name of the header to set is not permitted, the header was not added.");
- }
- else
- {
- string trimmedName = headerName.Trim();
- string trimmedValue = (headerValue != null) ? headerValue.Trim() : String.Empty;
- /*if (m_customSIPHeaders.Contains(trimmedName)) {
- m_customSIPHeaders[trimmedName] = trimmedValue;
- }
- else {
- m_customSIPHeaders.Add(trimmedName, trimmedValue);
- }*/
- m_customSIPHeaders.Add(trimmedName + ": " + trimmedValue);
- Log("Custom SIP header " + trimmedName + " successfully added to list.");
- }
- }
-
- /// <summary>
- /// If present removes a SIP header from the list of custom headers.
- /// </summary>
- /// <param name="headerName">The name of the SIP header to remove.</param>
- public void RemoveCustomSIPHeader(string headerName)
- {
-
- if (!headerName.IsNullOrBlank() && m_customSIPHeaders.Contains(headerName.Trim()))
- {
- m_customSIPHeaders.Remove(headerName.Trim());
- Log("Custom SIP header " + headerName.Trim() + " successfully removed.");
- }
- else
- {
- Log("Custom SIP header " + headerName.Trim() + " was not in the list.");
- }
- }
-
- /// <summary>
- /// Clears all the custom SIP header values from the list.
- /// </summary>
- public void ClearCustomSIPHeaders()
- {
- m_customSIPHeaders.Clear();
- }
-
- /// <summary>
- /// Dumps the currently stored custom SIP headers to the console or monitoring screen to allow
- /// users to troubleshoot.
- /// </summary>
- public void PrintCustomSIPHeaders()
- {
- Log("Custom SIP Header List:");
- foreach (string customHeader in m_customSIPHeaders)
- {
- Log(" " + customHeader);
- }
- }
-
- /// <summary>
- /// Sets the value of part or all of the From header that will be set on forwarded calls. Leaving a part of the
- /// header as null will result in the corresponding value from the originating request being used.
- /// </summary>
- /// <param name="fromName">The custom From header display name to set.</param>
- /// <param name="fromUser">The custom From header URI user value to set.</param>
- /// <param name="fromHost">The custom From header URI host value to set.</param>
- public void SetFromHeader(string fromName, string fromUser, string fromHost)
- {
- m_customFromName = fromName;
- m_customFromUser = fromUser;
- m_customFromHost = fromHost;
- }
-
- /// <summary>
- /// Reset the custom From header values so that the corresponding values from the originating request will
- /// be used.
- /// </summary>
- public void ClearFromHeader()
- {
- m_customFromName = null;
- m_customFromUser = null;
- m_customFromHost = null;
- }
-
- /// <summary>
- /// Sets the custom body that will override the incoming request body for forwarded INVITE requests.
- /// </summary>
- /// <param name="body">The custom body that will be sent in forwarded INVITE requests.</param>
- public void SetCustomContent(string content)
- {
- m_customContent = content;
- }
-
- /// <summary>
- /// Sets the custom body that will override the incoming request body for forwarded INVITE requests.
- /// </summary>
- /// <param name="body">The custom body that will be sent in forwarded INVITE requests.</param>
- public void SetCustomContent(string contentType, string content)
- {
- m_customContentType = contentType;
- m_customContent = content;
- }
-
- /// <summary>
- /// Clears the custom body so that the incoming request body will again be used on forwarded requests.
- /// </summary>
- public void ClearCustomBody()
- {
- m_customContentType = null;
- m_customContent = null;
- }
-
- public void GTalk(string username, string password, string sendToUser, string message)
- {
- GTalk(username, password, sendToUser, message, GTALK_DEFAULT_CONNECTION_TIMEOUT);
- }
-
- /// <summary>
- /// Attempts to send a gTalk IM to the specified account.
- /// </summary>
- public void GTalk(string username, string password, string sendToUser, string message, int connectionTimeout)
- {
- try
- {
- if (connectionTimeout > GTALK_MAX_CONNECTION_TIMEOUT)
- {
- connectionTimeout = GTALK_MAX_CONNECTION_TIMEOUT;
- }
- else if (connectionTimeout < GTALK_DEFAULT_CONNECTION_TIMEOUT)
- {
- connectionTimeout = GTALK_DEFAULT_CONNECTION_TIMEOUT;
- }
-
- XmppClientConnection xmppCon = null;
-
- if (m_gtalkConnections.ContainsKey(username))
- {
- xmppCon = m_gtalkConnections[username];
- Log("Using existing gTalk connection for " + username + "@gmail.com.");
- xmppCon.Send(new Message(new Jid(sendToUser + "@gmail.com"), MessageType.chat, message));
- }
- else
- {
- xmppCon = new XmppClientConnection();
- xmppCon.Password = password;
- xmppCon.Username = username;
- xmppCon.Server = "gmail.com";
- xmppCon.ConnectServer = "talk.google.com";
- xmppCon.AutoAgents = false;
- xmppCon.AutoPresence = false;
- xmppCon.AutoRoster = false;
- xmppCon.AutoResolveConnectServer = true;
-
- Log("Attempting to connect to gTalk for " + username + ".");
-
- ManualResetEvent waitForConnect = new ManualResetEvent(false);
-
- xmppCon.OnLogin += new ObjectHandler((sender) => waitForConnect.Set());
- xmppCon.Open();
-
- if (waitForConnect.WaitOne(connectionTimeout, false))
- {
- Log("Connected to gTalk for " + username + "@gmail.com.");
- if (!m_gtalkConnections.ContainsKey(username))
- {
- m_gtalkConnections.Add(username, xmppCon);
- }
- xmppCon.Send(new Message(new Jid(sendToUser + "@gmail.com"), MessageType.chat, message));
- }
- else
- {
- Log("Connection to gTalk for " + username + " timed out.");
- }
- }
- //xmppCon.Close();
- }
- catch (Exception excp)
- {
- logger.Error("Exception GTalk. " + excp.Message);
- Log("Exception GTalk. " + excp.Message);
- }
- }
-
- public void GoogleVoiceCall(string emailAddress, string password, string forwardingNumber, string destinationNumber)
- {
- GoogleVoiceCall(emailAddress, password, forwardingNumber, destinationNumber, null, DEFAULT_GOOGLEVOICE_PHONETYPE, 0);
- }
-
- public void GoogleVoiceCall(string emailAddress, string password, string forwardingNumber, string destinationNumber, string fromURIUserToMatch)
- {
- GoogleVo…
Large files files are truncated, but you can click here to view the full file