public void Call(SIPCallDescriptor sipCallDescriptor)
{
try
{
m_sipCallDescriptor = sipCallDescriptor;
SIPURI callURI = SIPURI.ParseSIPURI(sipCallDescriptor.Uri);
SIPRouteSet routeSet = null;
if (!m_callCancelled)
{
// If the outbound proxy is a loopback address, as it will normally be for local deployments, then it cannot be overriden.
if (m_outboundProxy != null && IPAddress.IsLoopback(m_outboundProxy.Address))
{
m_serverEndPoint = m_outboundProxy;
}
else if (!sipCallDescriptor.ProxySendFrom.IsNullOrBlank())
{
// If the binding has a specific proxy end point sent then the request needs to be forwarded to the proxy's default end point for it to take care of.
SIPEndPoint outboundProxyEndPoint = SIPEndPoint.ParseSIPEndPoint(sipCallDescriptor.ProxySendFrom);
m_outboundProxy = new SIPEndPoint(SIPProtocolsEnum.udp, new IPEndPoint(outboundProxyEndPoint.Address, m_defaultSIPPort));
m_serverEndPoint = m_outboundProxy;
Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.UserAgentClient, SIPMonitorEventTypesEnum.DialPlan, "SIPClientUserAgent Call using alternate outbound proxy of " + m_outboundProxy + ".", Owner));
}
else if (m_outboundProxy != null)
{
// Using the system outbound proxy only, no additional user routing requirements.
m_serverEndPoint = m_outboundProxy;
}
// A custom route set may have been specified for the call.
if (m_sipCallDescriptor.RouteSet != null && m_sipCallDescriptor.RouteSet.IndexOf(OUTBOUNDPROXY_AS_ROUTESET_CHAR) != -1)
{
try
{
routeSet = new SIPRouteSet();
routeSet.PushRoute(new SIPRoute(m_sipCallDescriptor.RouteSet, true));
}
catch
{
Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.UserAgentClient, SIPMonitorEventTypesEnum.DialPlan, "Error an outbound proxy value was not recognised in SIPClientUserAgent Call. " + m_sipCallDescriptor.RouteSet + ".", Owner));
}
}
// No outbound proxy, determine the forward destination based on the SIP request.
if (m_serverEndPoint == null)
{
SIPDNSLookupResult lookupResult = null;
if (routeSet == null || routeSet.Length == 0)
{
Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.UserAgentClient, SIPMonitorEventTypesEnum.DialPlan, "Attempting to resolve " + callURI.Host + ".", Owner));
lookupResult = m_sipTransport.GetURIEndPoint(callURI, false);
}
else
{
Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.UserAgentClient, SIPMonitorEventTypesEnum.DialPlan, "Route set for call " + routeSet.ToString() + ".", Owner));
lookupResult = m_sipTransport.GetURIEndPoint(routeSet.TopRoute.URI, false);
}
if (lookupResult.LookupError != null)
{
Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.UserAgentClient, SIPMonitorEventTypesEnum.DialPlan, "DNS error resolving " + callURI.Host + ", " + lookupResult.LookupError + ". Call cannot proceed.", Owner));
}
else
{
m_serverEndPoint = lookupResult.GetSIPEndPoint();
}
}
if (m_callCancelled)
{
Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.UserAgentClient, SIPMonitorEventTypesEnum.DialPlan, "Call was cancelled during DNS resolution of " + callURI.Host, Owner));
FireCallFailed(this, "Cancelled by caller");
}
else if (m_serverEndPoint != null)
{
Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.UserAgentClient, SIPMonitorEventTypesEnum.DialPlan, "Switching to " + SIPURI.ParseSIPURI(m_sipCallDescriptor.Uri).CanonicalAddress + " via " + m_serverEndPoint + ".", Owner));
m_localSIPEndPoint = m_sipTransport.GetDefaultSIPEndPoint(m_serverEndPoint);
if (m_localSIPEndPoint == null)
{
throw new ApplicationException("The call could not locate an appropriate SIP transport channel for protocol " + callURI.Protocol + ".");
}
string content = sipCallDescriptor.Content;
if (content.IsNullOrBlank())
{
Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Body on UAC call was empty.", Owner));
}
else if (m_sipCallDescriptor.ContentType == m_sdpContentType)
{
if (!m_sipCallDescriptor.MangleResponseSDP)
{
IPEndPoint sdpEndPoint = SDP.GetSDPRTPEndPoint(content);
if (sdpEndPoint != null)
{
Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "SDP on UAC call was set to NOT mangle, RTP socket " + sdpEndPoint.ToString() + ".", Owner));
}
else
{
Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "SDP on UAC call was set to NOT mangle, RTP socket could not be determined.", Owner));
}
}
else
{
IPEndPoint sdpEndPoint = SDP.GetSDPRTPEndPoint(content);
if (sdpEndPoint != null)
{
if (!IPSocket.IsPrivateAddress(sdpEndPoint.Address.ToString()))
{
Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "SDP on UAC call had public IP not mangled, RTP socket " + sdpEndPoint.ToString() + ".", Owner));
}
else
{
bool wasSDPMangled = false;
if (sipCallDescriptor.MangleIPAddress != null)
{
if (sdpEndPoint != null)
{
content = SIPPacketMangler.MangleSDP(content, sipCallDescriptor.MangleIPAddress.ToString(), out wasSDPMangled);
}
}
if (wasSDPMangled)
{
Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "SDP on UAC call had RTP socket mangled from " + sdpEndPoint.ToString() + " to " + sipCallDescriptor.MangleIPAddress.ToString() + ":" + sdpEndPoint.Port + ".", Owner));
}
else if (sdpEndPoint != null)
{
Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "SDP on UAC could not be mangled, using original RTP socket of " + sdpEndPoint.ToString() + ".", Owner));
}
}
}
else
{
Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "SDP RTP socket on UAC call could not be determined.", Owner));
}
}
}
SIPRequest switchServerInvite = GetInviteRequest(m_sipCallDescriptor, CallProperties.CreateBranchId(), CallProperties.CreateNewCallId(), m_localSIPEndPoint, routeSet, content, sipCallDescriptor.ContentType);
// Now that we have a destination socket create a new UAC transaction for forwarded leg of the call.
m_serverTransaction = m_sipTransport.CreateUACTransaction(switchServerInvite, m_serverEndPoint, m_localSIPEndPoint, m_outboundProxy);
m_serverTransaction.CDR.DialPlanContextID = m_sipCallDescriptor.DialPlanContextID;
#region Real-time call control processing.
string rtccError = null;
if (m_serverTransaction.CDR != null)
{
m_serverTransaction.CDR.Owner = Owner;
m_serverTransaction.CDR.AdminMemberId = AdminMemberId;
m_serverTransaction.CDR.Updated();
#if !SILVERLIGHT && !MIN_BUILD
if (m_sipCallDescriptor.AccountCode != null)
{
var customerAccount = m_customerAccountDataLayer.CheckAccountCode(Owner, m_sipCallDescriptor.AccountCode);
if (customerAccount == null)
{
Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "A billable call could not proceed as no account exists for account code or number " + m_sipCallDescriptor.AccountCode + ".", Owner));
rtccLogger.Debug("A billable call could not proceed as no account exists for account code or number " + m_sipCallDescriptor.AccountCode + " and owner " + Owner + ".");
rtccError = "Real-time call control invalid account code";
}
else
{
AccountCode = customerAccount.AccountCode;
string rateDestination = m_sipCallDescriptor.Uri;
if (SIPURI.TryParse(m_sipCallDescriptor.Uri))
{
rateDestination = SIPURI.ParseSIPURIRelaxed(m_sipCallDescriptor.Uri).User;
}
var rate = m_customerAccountDataLayer.GetRate(Owner, m_sipCallDescriptor.RateCode, rateDestination, customerAccount.RatePlan);
if (rate == null)
{
Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "A billable call could not proceed as no rate could be determined for destination " + rateDestination + ".", Owner));
rtccLogger.Debug("A billable call could not proceed as no rate could be determined for destination " + rateDestination + " and owner " + Owner + ".");
rtccError = "Real-time call control no rate";
}
else
{
Rate = rate.Rate1;
if (rate.Rate1 == 0 && rate.SetupCost == 0)
{
Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "The rate and setup cost for the " + rateDestination + "were both zero. The call will be allowed to proceed with no RTCC reservation.", Owner));
}
else
{
decimal balance = m_customerAccountDataLayer.GetBalance(AccountCode);
if (balance < Rate)
{
Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "A billable call could not proceed as the available credit for " + AccountCode + " was not sufficient for 60 seconds to destination " + rateDestination + ".", Owner));
rtccLogger.Debug("A billable call could not proceed as the available credit for " + AccountCode + " was not sufficient for 60 seconds to destination " + rateDestination + " and owner " + Owner + ".");
rtccError = "Real-time call control insufficient credit";
}
else
{
int intialSeconds = 0;
var reservationCost = m_customerAccountDataLayer.ReserveInitialCredit(AccountCode, rate, m_serverTransaction.CDR, out intialSeconds);
if (reservationCost == Decimal.MinusOne)
{
Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Call will not proceed as the intial real-time call control credit reservation failed.", Owner));
rtccLogger.Debug("Call will not proceed as the intial real-time call control credit reservation failed for owner " + Owner + ".");
rtccError = "Real-time call control initial reservation failed";
}
else
{
ReservedCredit = reservationCost;
ReservedSeconds = intialSeconds;
}
}
}
}
}
}
#endif
}
// If this is a billable call attempt to reserve the first chunk of credit.
//if (m_serverTransaction.CDR != null && AccountCode.NotNullOrBlank())
//{
// m_serverTransaction.CDR.AccountCode = AccountCode;
// m_serverTransaction.CDR.Rate = Rate;
// //m_serverTransaction.CDR.Cost = ReservedCredit;
// //m_serverTransaction.CDR.SecondsReserved = ReservedSeconds;
// var reservationCost = m_customerAccountDataLayer.ReserveInitialCredit(AccountCode, Rate, m_rtccInitialReservationSeconds);
// if (reservationCost == Decimal.MinusOne)
// {
// Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Call will not proceed as the intial real-time call control credit reservation failed.", Owner));
// }
// else
// {
// m_serverTransaction.CDR.SecondsReserved = m_rtccInitialReservationSeconds;
// m_serverTransaction.CDR.Cost = reservationCost;
// }
//}
#endregion
if (rtccError == null)
{
m_serverTransaction.UACInviteTransactionInformationResponseReceived += ServerInformationResponseReceived;
m_serverTransaction.UACInviteTransactionFinalResponseReceived += ServerFinalResponseReceived;
m_serverTransaction.UACInviteTransactionTimedOut += ServerTimedOut;
m_serverTransaction.TransactionTraceMessage += TransactionTraceMessage;
m_serverTransaction.SendInviteRequest(m_serverEndPoint, m_serverTransaction.TransactionRequest);
}
else
{
m_serverTransaction.CancelCall(rtccError);
FireCallFailed(this, rtccError);
}
}
else
{
if (routeSet == null || routeSet.Length == 0)
{
Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.UserAgentClient, SIPMonitorEventTypesEnum.DialPlan, "Forward leg failed, could not resolve URI host " + callURI.Host, Owner));
m_serverTransaction.CancelCall("Unresolvable destination " + callURI.Host);
FireCallFailed(this, "unresolvable destination " + callURI.Host);
}
else
{
Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.UserAgentClient, SIPMonitorEventTypesEnum.DialPlan, "Forward leg failed, could not resolve top Route host " + routeSet.TopRoute.Host, Owner));
m_serverTransaction.CancelCall("Unresolvable destination " + routeSet.TopRoute.Host);
FireCallFailed(this, "unresolvable destination " + routeSet.TopRoute.Host);
}
}
}
}
catch (ApplicationException appExcp)
{
if (m_serverTransaction != null)
{
m_serverTransaction.CancelCall(appExcp.Message);
}
FireCallFailed(this, appExcp.Message);
}
catch (Exception excp)
{
Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.UserAgentClient, SIPMonitorEventTypesEnum.DialPlan, "Exception UserAgentClient Call. " + excp.Message, Owner));
if (m_serverTransaction != null)
{
m_serverTransaction.CancelCall("Unknown exception");
}
FireCallFailed(this, excp.Message);
}
}