2024-02-22 16:43:59 +00:00
using Independentsoft.Sip ;
using Independentsoft.Sip.Methods ;
using Independentsoft.Sip.Responses ;
using Independentsoft.Sip.Sdp ;
using SocketIOComponent ;
using System ;
using System.Collections.Generic ;
using System.Collections.ObjectModel ;
using System.Globalization ;
using System.Linq ;
using System.Net ;
using System.Net.Sockets ;
using System.Text ;
using System.Threading ;
using System.Threading.Tasks ;
namespace SipComponent
{
/// <summary>
/// Class used to make SD talk to Linx using Adi's protocol, version 2
/// <para>Accepts more than one sip call at a time</para>
/// <para>Does not use socket.IO requests for calls</para>
/// </summary>
public class SipClientClass2
{
#region Private Fields
private SipClient _sipClient ;
private int _registrationInterval = 100 ; // 100s
private RegistrationData _registrationData ;
private int _minPortNb = 25284 ;
private int _maxPortNb = 25300 ;
private int _audioBitRate = 16 ;
private int _maxNbOfDialogs = 3 ;
private int _secondsToWaitForSmsConfirmation = 3 ;
private int _sipDomainPort ;
/// <summary>
/// Nr de secunde cat este mentinut call-ul cand nu se transmite voce
/// </summary>
private int _hangTimeDuration = - 1 ;
private int _bufferMiliseconds ;
private string _localIPaddress ;
private Random _rand = new Random ( ) ;
private bool _sipClassClosed = false ;
/// <summary>
/// Gets or sets a value indicating if the Asterisk server confirms sms
/// <para>Default value is false</para>
/// </summary>
protected bool _smsConfirmationFromServer = false ;
private Dictionary < string , Invite > _IDsentInviteDict = new Dictionary < string , Invite > ( ) ;
private Dictionary < string , Request > _IDreceivedInviteDict = new Dictionary < string , Request > ( ) ;
private Dictionary < string , Tuple < UdpClient , RTPSender2 , RTPListener2 , TimerWithTag < string > > > _IDdialogTuple = new Dictionary < string , Tuple < UdpClient , RTPSender2 , RTPListener2 , TimerWithTag < string > > > ( ) ;
private Dictionary < string , bool? > _smsSentDict = new Dictionary < string , bool? > ( ) ;
private Dictionary < string , Tuple < System . Timers . Timer , RegistrationStatus > > _sipID_regTimer_regStatus_Dict =
new Dictionary < string , Tuple < System . Timers . Timer , RegistrationStatus > > ( ) ;
private Dictionary < string , Tuple < byte [ ] , TimerWithTag < string > > > _ID_pingBytest_timer_dict = new Dictionary < string , Tuple < byte [ ] , TimerWithTag < string > > > ( ) ;
private Dictionary < string , string > _simocoID_emergencyStatusReport_dict = new Dictionary < string , string > ( ) ;
private object _lockerSmsSet = new object ( ) ;
private List < string > _IDsCalledByMeList = new List < string > ( ) ;
private List < string > _IDsCallingMeList = new List < string > ( ) ;
private List < string > _IDsInDialogWithList = new List < string > ( ) ;
private List < string > _IDsregisteredList = new List < string > ( ) ;
private static object _lockerRtpPort = new object ( ) ;
private object _lockerSipDialog = new object ( ) ;
private object _lockerCallingMe = new object ( ) ;
#region Protection against multiple sip messages sent ( sometimes ) by Asterisk
string _previousEmergencySeqID = "" ;
string _previousLocationSeqID = "" ;
string _previousArsSeqID = "" ;
string _previousSmsSeqID = "" ;
#endregion
SocketIOClass _socketIOClass = null ;
bool _sendArsOnOff = true ;
HashSet < string > _linxGroupSipIDsInDialogWith = new HashSet < string > ( ) ;
#endregion
#region Public Properties
/// <summary>
/// Gets or sets the minimum port number in the port range used for RTP voice transmission
/// </summary>
public int MinRtpPortNumber
{
get { return _minPortNb ; }
set
{
if ( ValidRtpPort ( value ) )
{
_minPortNb = value ;
}
else
throw new SipClassException (
string . Format ( "Port {0} is not a valid RTP port" , value ) ) ;
}
}
/// <summary>
/// Gets or sets the maximum port number in the port range used for RTP voice transmission
/// </summary>
public int MaxRtpPortNumber
{
get { return _maxPortNb ; }
set
{
if ( ValidRtpPort ( value ) )
{
_maxPortNb = value ;
}
else
throw new SipClassException (
string . Format ( "Port {0} is not a valid RTP port" , value ) ) ;
}
}
/// <summary>
/// Gets the sip ID
/// </summary>
public string UserName
{
get { return _sipClient . Username ; }
}
/// <summary>
/// <para>Gets or sets the maximum number of simultaneous dialogs that the SipClientClass accepts</para>
/// <para>The default is 3</para>
/// </summary>
public int MaxNbOfDialogs
{
get { return _maxNbOfDialogs ; }
set
{
_maxNbOfDialogs = value ;
}
}
/// <summary>
/// <para>Gets or sets the number of seconds the call is maintained when no voice is transmited</para>
/// <para>Value is in seconds</para>
/// <para>Default value is -1, call is maintained indefinitely</para>
/// </summary>
public int HangTimeDuration
{
get { return _hangTimeDuration ; }
set
{
if ( value > 0 )
_hangTimeDuration = value ;
}
}
/// <summary>
/// <para>Gets or sets a value indicating the nb of seconds to wait</para>
/// <para>for a sms confirmation from sip server</para>
/// <para>Default value is 3</para>
/// </summary>
public int SecondsToWaitForSmsConfirmation
{
get { return _secondsToWaitForSmsConfirmation ; }
set
{
if ( value > 0 & & value < 10 )
_secondsToWaitForSmsConfirmation = value ;
}
}
/// <summary>
/// Audio BitRate, default value is 16
/// </summary>
public int AudioBitrate
{
get { return _audioBitRate ; }
set { _audioBitRate = value ; }
}
/// <summary>
/// Gets a readonly collection of sip ids that you sent Invite for
/// </summary>
public ReadOnlyCollection < string > SipIDsCalledByMe
{
get { return new ReadOnlyCollection < string > ( _IDsCalledByMeList ) ; }
}
/// <summary>
/// Gets a readonly collection of sip ids that sent you Invite's
/// </summary>
public ReadOnlyCollection < string > SipIDsCallingMe
{
get { return new ReadOnlyCollection < string > ( _IDsCallingMeList ) ; }
}
/// <summary>
/// Gets a readonly collection of sip ids that you are in dialog with
/// </summary>
public ReadOnlyCollection < string > SipIDsInDialogWith
{
get { return new ReadOnlyCollection < string > ( _IDsInDialogWithList ) ; }
}
/// <summary>
/// Gets a readonly collection of sip ids that you are registered to
/// <para>This means your sip id and the group ids that you are listening to</para>
/// </summary>
public ReadOnlyCollection < string > SipIDsRegisteredTo
{
get
{
return new ReadOnlyCollection < string > ( _IDsregisteredList ) ;
}
}
/// <summary>
/// Gets or sets a value indicating if the Asterisk sends sms confirmations on delivery
/// <para>Default value is false</para>
/// </summary>
public bool SmsConfirmationFromAsterisk
{
get { return _smsConfirmationFromServer ; }
set { _smsConfirmationFromServer = true ; }
}
#endregion
#region Constructor
// constructor
/// <summary>
/// Constructor for the SipClientClass
/// </summary>
/// <param name="sipDomain">domain name, name or IP address of sip server</param>
/// <param name="sipDomainPort">port number of the sip server</param>
/// <param name="localSipPort">port number of the local computer used for sip protocol</param>
/// <param name="userName">user name on the sip server</param>
/// <param name="password">password on the sip server</param>
/// <param name="registrationInterval">interval to send sip registration requests. Value is in seconds</param>
/// <param name="bufferMiliseconds">Miliseconds for the buffer that stores the received voice packets</param>
/// <param name="requestTimeout">Number of ms to wait before the sip request times out</param>
/// <param name="socketIOport">Port number use for socket.IO</param>
/// <param name="sendArsOnOff">True to send ars on on creation and off when calling Stop()</param>
/// <param name="localIPAddress">Local Ip adress. If not specified, the class will search for a local ip on the same network with the sip server ip</param>
public SipClientClass2 ( string sipDomain , int sipDomainPort , int localSipPort , string userName , string password , int registrationInterval , int bufferMiliseconds ,
int requestTimeout , int socketIOport , bool sendArsOnOff , string localIPAddress = null )
{
if ( localIPAddress = = null )
{
_localIPaddress = BestLocalEndPoint ( new IPEndPoint ( IPAddress . Parse ( sipDomain ) , sipDomainPort ) ) . Address . ToString ( ) ;
#if DEBUG
Console . WriteLine ( "Local IP selected: " + _localIPaddress ) ;
#endif
}
else
_localIPaddress = localIPAddress ;
2024-06-20 16:03:35 +00:00
2024-02-22 16:43:59 +00:00
_sipDomainPort = sipDomainPort ;
_sipClient = CreateSipClientClass ( sipDomain , _sipDomainPort , _localIPaddress , localSipPort , userName , password , requestTimeout ) ;
_bufferMiliseconds = bufferMiliseconds ;
2024-06-20 16:03:35 +00:00
2024-02-22 16:43:59 +00:00
// Registration Timer
_registrationInterval = registrationInterval ;
_registrationData = new RegistrationData ( userName , _registrationInterval + 2 ) ;
2024-06-20 16:03:35 +00:00
2024-02-22 16:43:59 +00:00
System . Timers . Timer registrationTimer = new System . Timers . Timer ( ) ;
// Set up the registration timer
registrationTimer . Interval = _registrationInterval * 1000 ;
registrationTimer . Elapsed + = _registrationTimer_Elapsed ;
2024-06-20 16:03:35 +00:00
2024-02-22 16:43:59 +00:00
_sipID_regTimer_regStatus_Dict . Add ( userName ,
new Tuple < System . Timers . Timer , RegistrationStatus > ( registrationTimer , RegistrationStatus . RegistrationNotStarted
) ) ;
2024-06-20 16:03:35 +00:00
2024-02-22 16:43:59 +00:00
_IDsregisteredList . Add ( userName ) ;
StartRegistrationTimer ( ) ;
// Socket IO
this . _socketIOClass = new SocketIOClass ( $"ws://{sipDomain}:{socketIOport}" ) ;
this . _socketIOClass . SmsReceived + = _socketIOClass_SmsReceived ;
this . _socketIOClass . SmsAckReceived + = _socketIOClass_SmsAckReceived ;
// Send ARS ON after 1 second since the socketIOClass instantiation
_sendArsOnOff = sendArsOnOff ;
if ( _sendArsOnOff )
Task . Delay ( 1000 ) . ContinueWith ( t = > { this . _socketIOClass . SendArs ( new ArsInfo ( true , "0" , userName ) ) ; } ) ;
}
#endregion
#region Public Methods
/// <summary>
/// Sends a Sip Invite command to an user
/// </summary>
/// <param name="idToInvite">The sip id of the user</param>
public void Invite ( string idToInvite )
{
if ( _sipClassClosed )
throw new ObjectDisposedException ( "SipClientClass" ) ;
if ( idToInvite = = null )
throw new ArgumentNullException ( "idToInvite" ) ;
2024-06-20 16:03:35 +00:00
2024-02-22 16:43:59 +00:00
lock ( _lockerSipDialog )
{
if ( ! _IDsentInviteDict . ContainsKey ( idToInvite ) & & ! _IDreceivedInviteDict . ContainsKey ( idToInvite )
& & ! _IDdialogTuple . ContainsKey ( idToInvite ) )
{
SendSipInvite ( idToInvite , false ) ;
}
}
}
/// <summary>
/// Sends a Sip Invite command to a Simoco Group
/// </summary>
/// <param name="groupIDtoInvite">The sip id of the group</param>
public void InviteGroup ( string groupIDtoInvite )
{
if ( _sipClassClosed )
throw new ObjectDisposedException ( "SipClientClass" ) ;
if ( groupIDtoInvite = = null )
throw new ArgumentNullException ( "groupIDtoInvite" ) ;
2024-06-20 16:03:35 +00:00
2024-02-22 16:43:59 +00:00
// Check if registered to the group id
lock ( _lockerSipDialog )
{
if ( ! _linxGroupSipIDsInDialogWith . Contains ( groupIDtoInvite ) )
SendSipInvite ( groupIDtoInvite , true ) ;
else
OnError ( new ErrorEventArgs ( groupIDtoInvite , UserName , $"Cannot call group {groupIDtoInvite} while I receive voice from it" ) ) ;
}
}
/// <summary>
/// Cancels an Invite sent to an user
/// </summary>
/// <param name="idToCancel">The sip id of the user</param>
public void CancelInvite ( string idToCancel )
{
if ( _sipClassClosed )
throw new ObjectDisposedException ( "SipClientClass" ) ;
2024-06-20 16:03:35 +00:00
2024-02-22 16:43:59 +00:00
Task t = null ;
CancelInvite_private ( idToCancel , out t ) ;
}
private void CancelInvite_private ( string idToCancel , out Task sendingCancelTask )
{
sendingCancelTask = null ;
lock ( _lockerSipDialog )
{
if ( _IDsentInviteDict . ContainsKey ( idToCancel ) )
{
sendingCancelTask = SendCancelRequest ( idToCancel , sendingCancelTask ) ;
}
}
// fire event
OnInviteSentCanceled ( new SipEventArgs ( idToCancel ) ) ;
}
private Task SendCancelRequest ( string idToCancel , Task sendingCancelTask )
{
// Send Cancel Request
if ( _IDsentInviteDict . ContainsKey ( idToCancel ) )
{
Dialog d = _sipClient . Dialogs . FirstOrDefault ( ( dialog ) = >
{
return dialog . CallID = = _IDsentInviteDict [ idToCancel ] . CallID ;
} ) ;
2024-06-20 16:03:35 +00:00
2024-02-22 16:43:59 +00:00
if ( d ! = null )
{
//Send Cancel Request
sendingCancelTask = Task . Factory . StartNew ( ( dialogObj ) = >
{
try
{
_sipClient . Cancel ( ( Dialog ) dialogObj ) ;
}
catch ( Independentsoft . Sip . TimeoutException )
{
;
// Don't to anything if timeout exception
// the receiver did not answer;
}
} , d ) ;
}
2024-06-20 16:03:35 +00:00
2024-02-22 16:43:59 +00:00
// Remove from dict
_IDsentInviteDict . Remove ( idToCancel ) ;
_IDsCalledByMeList . Remove ( idToCancel ) ;
}
return sendingCancelTask ;
}
/// <summary>
/// Accepts an Invite from an user
/// </summary>
/// <param name="callingSipID">The sip ID of the user</param>
public void AcceptInvite ( string callingSipID )
{
if ( _sipClassClosed )
throw new ObjectDisposedException ( "SipClientClass" ) ;
bool fireEvent = false ;
bool success = true ;
bool sendBusy = true ;
string failedMsg = "" ;
lock ( _lockerCallingMe )
{
if ( _IDreceivedInviteDict . ContainsKey ( callingSipID ) )
{
fireEvent = true ;
Request receivedInvite = _IDreceivedInviteDict [ callingSipID ] ;
// Answer the call
OK okResponseToInvite = new OK ( ) ;
// TO DO - fara asta (adaugare header Contact) nu merge Simoco (Zoiper-ul e ok);
okResponseToInvite . Header [ StandardHeader . Contact ] = "<sip:" + _sipClient . Username + "@" + _sipClient . LocalIPEndPoint . ToString ( ) + ";transport=udp>" ;
// Generate SDP
int port = ReturnAvailablePort ( ) ;
okResponseToInvite . SessionDescription = CreateSDP ( port ) ;
// Get linxGroupID from received invite (if is a group invite)
string linxGroupID = null ;
if ( receivedInvite . Header . Contains ( "toGroupSipID" ) )
{
int linxGroupIDint ;
if ( int . TryParse ( receivedInvite . Header [ "toGroupSipID" ] , out linxGroupIDint ) )
linxGroupID = linxGroupIDint . ToString ( ) ;
}
// Try to create the dialog
try
{
CreatingDialog ( callingSipID , receivedInvite . SessionDescription , port , false , linxGroupID ) ;
// Trimit Ok
_sipClient . SendResponse ( okResponseToInvite , receivedInvite ) ;
}
catch ( SipClassException )
{
// TO DO - verify this
// This is the strange case of "Already in dialog with....."
// generated by Asterisk sending multiple invite request
// I should ignore the isssue (not send busy, not fire event)
sendBusy = false ;
fireEvent = false ;
success = false ;
}
catch ( Exception ex )
{
// Nu am reusit sa creez dialogul
ClosingDialog ( callingSipID , _sipClient . Username , out linxGroupID ) ;
failedMsg = ex . Message ;
success = false ;
}
finally
{
// Remove from dictionary
_IDreceivedInviteDict . Remove ( callingSipID ) ;
_IDsCallingMeList . Remove ( callingSipID ) ;
}
if ( ! success )
{
if ( sendBusy )
{
// Trimit Busy Here
Task . Factory . StartNew ( ( ) = > { _sipClient . SendResponseBusyHere ( receivedInvite ) ; } ) ;
}
}
}
}
// Fire event
if ( fireEvent )
{
if ( ! success )
OnError ( new ErrorEventArgs ( _sipClient . Username , callingSipID , failedMsg ) ) ;
}
}
/// <summary>
/// Closes a sip dialog (voice session) with an user
/// </summary>
/// <param name="idToClose">The sip id of the user</param>
public bool Close ( string idToClose )
{
bool dialogClosed = false ;
if ( _sipClassClosed )
throw new ObjectDisposedException ( "SipClientClass" ) ;
if ( idToClose ! = null )
{
Task t = null ;
dialogClosed = Close_private ( idToClose , out t , DialogClosedReason . NormalClosing ) ;
}
else throw new ArgumentNullException ( "idToClose" ) ;
return dialogClosed ;
}
private bool Close_private ( string idToClose , out Task sendingByeTask , DialogClosedReason reason )
{
bool fireEvent = false ;
sendingByeTask = null ;
string sipGroupID = null ;
lock ( _lockerSipDialog )
{
Dialog dialogToClose = _sipClient . GetDialogWith ( idToClose ) ;
if ( dialogToClose ! = null )
{
if ( ClosingDialog ( idToClose , _sipClient . Username , out sipGroupID ) )
{
fireEvent = true ;
sendingByeTask = Task . Factory . StartNew ( ( dialogToCloseObj ) = >
{
try
{
_sipClient . Bye ( ( Dialog ) dialogToCloseObj ) ;
}
catch ( Exception ex )
{
// To do
// For now, do nothing
;
}
} , dialogToClose ) ;
}
}
}
// FireEvent
if ( fireEvent )
OnDialogClosed ( new LinxDialogClosedEventArgs (
idToClose , _sipClient . Username , reason , sipGroupID ) ) ;
return fireEvent ;
}
/// <summary>
/// Declines a received Invite from an user
/// </summary>
/// <param name="idToDeclineCall">The sip ID of the user</param>
public void Decline ( string idToDeclineCall )
{
if ( _sipClassClosed )
throw new ObjectDisposedException ( "SipClientClass" ) ;
if ( idToDeclineCall ! = null )
{
Task t = null ;
Decline_private ( idToDeclineCall , out t ) ;
}
else
throw new ArgumentNullException ( "idToDeclineCall" ) ;
}
private void Decline_private ( string idToDeclineCall , out Task sendBusyTask )
{
bool fireEvent = false ;
sendBusyTask = null ;
lock ( _lockerCallingMe )
{
if ( _IDreceivedInviteDict . ContainsKey ( idToDeclineCall ) )
{
fireEvent = true ;
Request inviteReq = _IDreceivedInviteDict [ idToDeclineCall ] ;
// Remove from dictionary
_IDreceivedInviteDict . Remove ( idToDeclineCall ) ;
_IDsCallingMeList . Remove ( idToDeclineCall ) ;
// Send busy here
sendBusyTask = Task . Factory . StartNew ( ( ) = >
{
_sipClient . SendResponseBusyHere ( inviteReq ) ;
} ) ;
}
}
// Fire event
if ( fireEvent )
OnInviteReceivedDeclined ( new SipEventArgs ( idToDeclineCall ) ) ;
}
/// <summary>
/// Sends a voice buffer in a specified format to the user in dialog with
/// </summary>
/// <param name="idToSendVoice">The sip ID of the user</param>
/// <param name="audioBuffer">The audio buffer</param>
/// <param name="bufferLength">The length of the buffer</param>
/// <param name="format">The audio format of the buffer</param>
public virtual void SendAudio ( string idToSendVoice , byte [ ] audioBuffer , int bufferLength , AudioFormat format )
{
if ( _sipClassClosed )
throw new ObjectDisposedException ( "SipClientClass" ) ;
2024-06-20 16:03:35 +00:00
2024-02-22 16:43:59 +00:00
lock ( _lockerSipDialog )
{
if ( _IDdialogTuple . ContainsKey ( idToSendVoice ) )
{
_IDdialogTuple [ idToSendVoice ] . Item2 . SendAudio ( audioBuffer , bufferLength , format ) ;
}
}
}
/// <summary>
/// Method used to send Gps Request to a Linx device
/// </summary>
/// <param name="idToRequestGps">The sip id to send the gps request</param>
/// <param name="seqID">The sequence id</param>
protected void SendGpsRequest ( string idToRequestGps , string seqID )
{
if ( _sipClassClosed )
throw new ObjectDisposedException ( "SipClientClass" ) ;
2024-06-20 16:03:35 +00:00
if ( idToRequestGps = = null )
throw new ArgumentNullException ( "idToRequestGps" ) ;
// Send Gps request for Linx
// [#msgLen]#seqID#154#
string cmdText = string . Format ( "#{0}#154#" , seqID ) ;
string cmd = AddMsgLenForMBus ( cmdText ) ;
Message pollRequestForLinx = GenerateSipMessage ( idToRequestGps , UserName , _sipClient . Domain , _sipDomainPort , cmd ) ;
pollRequestForLinx . Header . Add ( "Ais-Service" , "mbus" ) ;
Task . Factory . StartNew ( ( ) = >
2024-02-22 16:43:59 +00:00
{
2024-06-20 16:03:35 +00:00
try
2024-02-22 16:43:59 +00:00
{
2024-06-20 16:03:35 +00:00
_sipClient . SendRequest ( pollRequestForLinx ) ;
}
catch ( Exception )
{
; // Probably timeout exception, do not do anything
}
} ) ;
2024-02-22 16:43:59 +00:00
}
/// <summary>
/// Method used to acknowledge an emergency alarm sent by a Linx device
/// </summary>
/// <param name="linxID">The sip id of the Linx device</param>
protected void AcknowledgeLinxEmergencyAlarm ( string linxID )
{
if ( _sipClassClosed )
throw new ObjectDisposedException ( "SipClientClass" ) ;
2024-06-20 16:03:35 +00:00
if ( linxID = = null )
throw new ArgumentNullException ( "linxID" ) ;
// Send [#msgLen]#seqID#238#
string textToSend = string . Format ( "#{0}#238#" , _rand . Next ( ) . ToString ( ) ) ;
string cmdToSend = AddMsgLenForMBus ( textToSend ) ;
Message sipMessage = GenerateSipMessage ( linxID , UserName , _sipClient . Domain , _sipDomainPort , cmdToSend ) ;
Task . Factory . StartNew ( ( ) = >
2024-02-22 16:43:59 +00:00
{
2024-06-20 16:03:35 +00:00
try
2024-02-22 16:43:59 +00:00
{
2024-06-20 16:03:35 +00:00
_sipClient . SendRequest ( sipMessage ) ;
}
catch ( Exception )
{
// Do nothing
}
} ) ;
2024-02-22 16:43:59 +00:00
}
/// <summary>
/// Method used to send an sms to sip id using the sip protocol
/// <para>This method does not block the calling thread while waiting for the confirmation</para>
/// </summary>
/// <param name="idToSendSMS">The sip id where to send the sms</param>
/// <param name="text">The sms text</param>
/// <returns>True if the sms was received, else returns false</returns>
public async Task < bool > SendSmsAsync ( string idToSendSMS , string text )
{
if ( _sipClassClosed )
throw new ObjectDisposedException ( "SipClientClass" ) ;
2024-06-20 16:03:35 +00:00
2024-02-22 16:43:59 +00:00
if ( idToSendSMS ! = null & & text ! = null )
{
bool sendSipMessage = true ;
string unconfirmedSmsKey = null ;
2024-06-20 16:03:35 +00:00
2024-02-22 16:43:59 +00:00
if ( _smsConfirmationFromServer )
{
unconfirmedSmsKey = idToSendSMS + text . GetHashCode ( ) ;
// Add smsKey to unconfirmed sms set
lock ( _lockerSmsSet )
{
if ( ! _smsSentDict . ContainsKey ( unconfirmedSmsKey ) )
{
_smsSentDict . Add ( unconfirmedSmsKey , null ) ;
}
else
{
// Already the same msg to the same id in the dict
// Will not send sip message, just wait for the later to be confirmed
sendSipMessage = false ;
}
}
}
2024-06-20 16:03:35 +00:00
2024-02-22 16:43:59 +00:00
if ( sendSipMessage )
{
// Create a new thread on which:
// 1. Send sip message
// 2. Wait for confirmation that the mess reach the server
// 3. Wait for confirmation that the mess reach the destination id
return await Task . Factory . StartNew ( ( ) = >
{
// Send Sms using sip library
try
{
//RequestResponse rr = _sipClient.SendMessage(
// string.Format("sip:{0}@{1}", _sipClient.Username, _sipClient.Domain),
// string.Format("sip:{0}@{1}", idToSendSMS, _sipClient.Domain),
// text);
Message sipMsg = GenerateSipMessage ( idToSendSMS , _sipClient . Username , _sipClient . Domain ,
_sipDomainPort , text ) ;
RequestResponse rr = _sipClient . SendRequest ( sipMsg ) ;
// Message got to the sip server
if ( rr . Response . Description = = "OK" | | rr . Response . Description = = "Accepted" )
{
if ( unconfirmedSmsKey ! = null )
{
if ( SmsConfirmedBySipServer ( unconfirmedSmsKey ) )
return true ;
else
return false ;
}
else
return true ;
}
else // Sip server did not accept the message
return false ;
}
catch ( Exception )
{
// Probably timeout exception, msg didn't reach the sip server
return false ;
}
} ) ;
}
else
return await Task . Factory . StartNew < bool > ( SmsConfirmedBySipServer , unconfirmedSmsKey ) ;
}
else if ( idToSendSMS = = null )
throw new ArgumentNullException ( "idToSendSMS" ) ;
else
throw new ArgumentNullException ( "text" ) ;
}
2024-06-20 16:03:35 +00:00
private Message GenerateSipMessage ( string destinationID , string senderID , string sipServer , int sipServerPort , string text )
2024-02-22 16:43:59 +00:00
{
2024-06-20 16:03:35 +00:00
Message sipMessage = new Message ( )
{
Uri = $"sip:{destinationID}@{sipServer}:{sipServerPort}" ,
From = new ContactInfo ( $"sip:{senderID}@{sipServer}" ) ,
To = new ContactInfo ( $"sip:{destinationID}@{sipServer}" ) ,
ContentType = "text/plain;charset=UTF-8" ,
Body = text
} ;
2024-02-22 16:43:59 +00:00
return sipMessage ;
}
private bool SmsConfirmedBySipServer ( object unconfirmedSmsKey )
{
bool smsConfirmed = true ;
bool timeoutNotExpired = true ;
string smsKey = ( string ) unconfirmedSmsKey ;
lock ( _lockerSmsSet )
{
while ( _smsSentDict . ContainsKey ( smsKey ) & & ! _smsSentDict [ smsKey ] . HasValue )
{
smsConfirmed = false ;
timeoutNotExpired = System . Threading . Monitor . Wait ( _lockerSmsSet , SecondsToWaitForSmsConfirmation * 1000 ) ;
if ( ! timeoutNotExpired )
{
// timer expired, no confiramtion received
// delete the message from set
_smsSentDict . Remove ( smsKey ) ;
break ;
}
}
}
if ( timeoutNotExpired ) // check confirmation status
{
lock ( _lockerSmsSet )
{
if ( _smsSentDict . ContainsKey ( smsKey ) & & _smsSentDict [ smsKey ] . HasValue )
{
// Check confirmation status
smsConfirmed = _smsSentDict [ smsKey ] . Value ;
// Remove from dictionary
_smsSentDict . Remove ( smsKey ) ;
}
else
throw new ApplicationException ( "Error on working with unconfirmed sms dict" ) ;
}
}
return smsConfirmed ;
}
/// <summary>
/// Sends an sms on socket IO
/// </summary>
/// <param name="seqID">Unique seqID of the sender. It should be in the format sipID.timestamp</param>
/// <param name="fromScID">SC ID of the sender</param>
/// <param name="destinationSipID">The sip ID of the destination</param>
/// <param name="destinationScID">SC ID of the destination</param>
/// <param name="text">The text</param>
/// <param name="isGroupSms">True if is group sms, else false</param>
public void SendSocketIOSms ( string seqID , int fromScID , int destinationSipID , int destinationScID , string text , bool isGroupSms )
{
_socketIOClass . SendSms ( new SmsInfo ( seqID , UserName , fromScID . ToString ( ) , destinationSipID . ToString ( ) , destinationScID . ToString ( ) , text , isGroupSms ) ) ;
}
/// <summary>
/// Mehod used to check if you are in dialog with a sip id
/// </summary>
/// <param name="sipID">The sip id</param>
/// <returns>True if you are in dialog, else returns false</returns>
public bool InDialogWith ( string sipID )
{
if ( _sipClassClosed )
throw new ObjectDisposedException ( "SipClientClass" ) ;
lock ( _lockerSipDialog )
{
return _IDdialogTuple . ContainsKey ( sipID ) ;
}
}
/// <summary>
/// Stops registration by sending unregister request to the sip server
/// <para>Releases all the used resources</para>
/// <param name="async">If true, method returns before all the resources are released</param>
/// </summary>
public virtual void Stop ( bool async = true )
{
if ( ! _sipClassClosed )
{
_sipClassClosed = true ;
2024-06-20 16:03:35 +00:00
2024-02-22 16:43:59 +00:00
// Send ARS OFF and disconnect from socket IO
if ( _sendArsOnOff )
_socketIOClass . SendArs ( new ArsInfo ( false , "0" , UserName ) ) ;
2024-06-20 16:03:35 +00:00
_socketIOClass . Disconect ( ) ;
2024-02-22 16:43:59 +00:00
//
List < string > sipIDs = new List < string > ( ) ;
Task task = null ;
Task t = Task . Factory . StartNew ( ( ) = >
{
// 1. Reject all calls not answered yet
sipIDs . Clear ( ) ;
lock ( _lockerCallingMe )
{
foreach ( string callingID in _IDreceivedInviteDict . Keys )
sipIDs . Add ( callingID ) ;
}
foreach ( string sipID in sipIDs )
{
Decline_private ( sipID , out task ) ;
if ( task ! = null )
task . Wait ( ) ;
}
// 2. Cancel all sent calls
sipIDs . Clear ( ) ;
lock ( _lockerSipDialog )
{
foreach ( string calledSipID in _IDsentInviteDict . Keys )
sipIDs . Add ( calledSipID ) ;
}
foreach ( string sipID in sipIDs )
{
CancelInvite_private ( sipID , out task ) ;
if ( task ! = null )
task . Wait ( ) ;
}
// 3. Close all curent dialogs
sipIDs . Clear ( ) ;
lock ( _lockerSipDialog )
{
foreach ( string idInDialog in _IDdialogTuple . Keys )
sipIDs . Add ( idInDialog ) ;
}
foreach ( string sipID in sipIDs )
{
Close_private ( sipID , out task , DialogClosedReason . NormalClosing ) ;
if ( task ! = null )
task . Wait ( ) ;
}
// Stop registering to sip server
StopRegisterToSipServer ( ) ;
// Unregister events
_sipClient . ReceiveRequest - = ProcessSipRequest ;
_sipClient . ReceiveResponse - = ProcessSipResponse ;
// Stop sip class
_sipClient . Disconnect ( ) ;
} ) ;
2024-06-20 16:03:35 +00:00
2024-02-22 16:43:59 +00:00
if ( ! async )
t . Wait ( ) ;
}
}
/// <summary>
/// Stops registration by sending unregister request to the sip server
/// </summary>
public void StopRegisterToSipServer ( )
{
// Stop registering to groups
List < string > idsToUnregisterFrom = new List < string > ( _IDsregisteredList ) ;
RegistrationData zeroRegistrationData = null ;
foreach ( string id in idsToUnregisterFrom )
{
if ( id ! = _sipClient . Username )
zeroRegistrationData = new RegistrationData ( id , 0 , true ) ;
else
zeroRegistrationData = new RegistrationData ( id , 0 ) ;
2024-06-20 16:03:35 +00:00
2024-02-22 16:43:59 +00:00
_sipID_regTimer_regStatus_Dict [ id ] . Item1 . Stop ( ) ;
SendSipRegister ( zeroRegistrationData ) ;
}
}
#endregion
#region RTP Listener Event Handlers
private void ReceivedVoice ( object sender , AudioEventArgs e )
{
OnVoiceReceived ( ( LinxAudioEventArgs ) e ) ;
}
void ExceptionThrown ( object sender , EventArgs e )
{
// Close the Sip Session with Bye
RTPListener2 rtpListener = ( RTPListener2 ) sender ;
Task t = null ;
Close_private ( rtpListener . SipIDinDialogWith . ToString ( ) , out t , DialogClosedReason . Error ) ;
}
#endregion
#region Private Sip Functions
private void ProcessSipResponse ( object sender , ResponseEventArgs e )
{
Response resp = e . Response ;
string responserRadioID = resp . To . Address . Split ( ':' , '@' ) [ 1 ] ;
switch ( resp . StatusCode )
{
case 200 : // OK response
if ( resp . CSeq . Contains ( "INVITE" ) ) // OK response for an Invite
{
bool success = false ;
string errorMsg = "" ;
string linxGroupID = null ;
// Gasesc Invite-ul pentru care am primit OK
lock ( _lockerSipDialog )
{
if ( _IDsentInviteDict . ContainsKey ( responserRadioID ) )
{
Invite sentInvite = _IDsentInviteDict [ responserRadioID ] ;
// Get sipGroupID from the sent invite (if is a group invite)
if ( sentInvite . Header . Contains ( "toGroupSipID" ) )
{
int linxGroupIDint ;
if ( int . TryParse ( sentInvite . Header [ "toGroupSipID" ] , out linxGroupIDint ) )
linxGroupID = linxGroupIDint . ToString ( ) ;
}
try
{
// Send sip Ack
_sipClient . Ack ( resp ) ;
// Creez Dialogul
CreatingDialog ( responserRadioID ,
resp . SessionDescription ,
sentInvite . SessionDescription . Media [ 0 ] . Port ,
true , linxGroupID ) ;
success = true ;
}
catch ( Exception ex )
{
// Nu am reusit sa setez dialogul
errorMsg = ex . Message ;
// Fac DialogClosed();
if ( ClosingDialog ( responserRadioID , _sipClient . Username , out linxGroupID ) )
{
// Trimit Bye
Task . Factory . StartNew ( ( respObj ) = >
{
_sipClient . Bye ( ( Response ) respObj ) ;
} , resp ) ;
}
}
finally
{
// Remove from sent invites dictionary
_IDsentInviteDict . Remove ( responserRadioID ) ;
_IDsCalledByMeList . Remove ( responserRadioID ) ;
}
}
else // OK primit pentru un Invite anulat...
{
errorMsg = "Received OK for a cancelled Invite" ;
// Trimit Bye sau Cancel???
// Din standard se pare ca Bye
//Task.Factory.StartNew((respObj) =>
//{
// _sipClient.Bye((Response)respObj);
//}, resp);
// TO DO - to verify this
// That weird situation with Asterisk sending multiple requests / response
// Don't do anything, just return
break ;
}
}
if ( success )
OnDialogCreated ( new LinxDialogCreatedEventArgs ( responserRadioID , _sipClient . Username , TypeOfCall . FULL_DUPLEX , linxGroupID ! = null , linxGroupID ) ) ;
else
OnError ( new ErrorEventArgs ( responserRadioID , _sipClient . Username , errorMsg ) ) ;
}
break ;
case 100 : // Trying
break ;
case 180 : // Ringing
break ;
case 401 : // Unauthorized (temporarely)
break ;
case 202 : // Accepted
break ;
default : // Probabil eroare
// trimit Ack
// I don't send ack because the sip.NET library does not send it to the correct udp port
// This happens only with Asterisk, for Linx project
// It use to work for simoco
//_sipClient.Ack(resp);
bool fireEvent = false ;
lock ( _lockerSipDialog )
{
if ( _IDsentInviteDict . ContainsKey ( responserRadioID ) )
{
_IDsentInviteDict . Remove ( responserRadioID ) ;
_IDsCalledByMeList . Remove ( responserRadioID ) ;
fireEvent = true ;
}
}
if ( fireEvent )
OnError ( new ErrorEventArgs ( responserRadioID , _sipClient . Username , resp . Description ) ) ;
break ;
}
}
private void ProcessSipRequest ( object sender , RequestEventArgs e )
{
Request req = e . Request ;
string requesterRadioId = req . From . Address . Split ( ':' , '@' ) [ 1 ] ;
string requesterTargetId = req . To . Address . Split ( ':' , '@' ) [ 1 ] ;
switch ( req . Method )
{
case SipMethod . Invite :
bool fireEvent = false ;
string idInDialogWith = requesterRadioId ;
bool isEmergencyCall = false ;
lock ( _lockerCallingMe )
{
if ( ! _IDreceivedInviteDict . ContainsKey ( requesterRadioId ) )
{
// Check if is emergency call (Simoco)
//if (req.Header.Contains(StandardHeader.Priority))
// if (req.Header[StandardHeader.Priority] == "emergency")
// isEmergencyCall = true;
if ( SendResponseRinging ( req , requesterRadioId ) )
{
// Sent response Ringing
_sipClient . SendResponseRinging ( req ) ;
// Add to dictionary
_IDreceivedInviteDict . Add ( idInDialogWith , req ) ;
_IDsCallingMeList . Add ( idInDialogWith ) ;
// fire event
fireEvent = true ;
}
else
{
_sipClient . SendResponseBusyHere ( req ) ;
}
}
else
{
// Invite primit de doua ori de la acelasi Id, putin probabil
// Trimit Busy
_sipClient . SendResponseBusyHere ( req ) ;
}
}
if ( fireEvent )
{
OnInviteReceived ( new InviteReceivedArgs ( idInDialogWith , requesterRadioId , TypeOfCall . FULL_DUPLEX , isEmergencyCall ) ) ;
}
break ;
case SipMethod . Bye :
// Accept the request
_sipClient . AcceptRequest ( req ) ;
string idToClose = requesterRadioId ;
string linxGroupID = null ;
if ( ClosingDialog ( idToClose , requesterRadioId , out linxGroupID ) )
{
// FireEvent;
OnDialogClosed ( new LinxDialogClosedEventArgs (
idToClose , requesterRadioId , DialogClosedReason . NormalClosing , linxGroupID ) ) ;
}
break ;
case SipMethod . Cancel :
// Accept the request
_sipClient . AcceptRequest ( req ) ;
fireEvent = false ;
// Check if Bye request is for an open dialog
lock ( _lockerCallingMe )
{
if ( _IDreceivedInviteDict . ContainsKey ( requesterRadioId ) )
{
// remove from dictionary
_IDreceivedInviteDict . Remove ( requesterRadioId ) ;
_IDsCallingMeList . Remove ( requesterRadioId ) ;
// Fire event
fireEvent = true ;
}
}
if ( fireEvent )
OnInviteReceivedCanceled ( new SipEventArgs ( requesterRadioId ) ) ;
break ;
case SipMethod . Ack :
if ( req . Header . Contains ( StandardHeader . Contact ) )
{
// We received an Invite, sent Ok and now we receive ack
idInDialogWith = requesterRadioId ;
// Dialog confirmed
if ( _IDdialogTuple . ContainsKey ( idInDialogWith ) )
{
linxGroupID = ( ( RTPListenerLinx ) _IDdialogTuple [ idInDialogWith ] . Item3 ) . LinxGroupID ;
OnDialogCreated ( new LinxDialogCreatedEventArgs (
requesterTargetId ,
requesterRadioId ,
_IDdialogTuple [ idInDialogWith ] . Item2 . TypeOfCall ,
linxGroupID ! = null ,
linxGroupID ) ) ;
}
}
break ;
case SipMethod . Message :
// Accept the request
_sipClient . AcceptRequest ( req ) ;
ProcessReceivedSipMessage ( req , requesterRadioId ) ;
break ;
case SipMethod . Info :
case SipMethod . Notify :
case SipMethod . Options :
case SipMethod . Prack :
case SipMethod . Publish :
case SipMethod . Refer :
case SipMethod . Register :
case SipMethod . Subscribe :
case SipMethod . Update :
default :
// Accept request
_sipClient . AcceptRequest ( req ) ;
break ;
}
}
/// <summary>
/// Method used to decide if the app is ringing or is sending Busy response to sender
/// <para>Default behaviour is to ring (sends Ringing response)</para>
/// </summary>
/// <param name="receivedInvite">The received invite</param>
/// <param name="senderSipId">The sender of the invite</param>
/// <returns>True to send Ringing response, false to send Busy Here</returns>
protected virtual bool SendResponseRinging ( Request receivedInvite , string senderSipId )
{
bool sendRinging = true ;
// Check that the invite is proper formatted
if ( receivedInvite . Header . ContainsKey ( "isPTT" ) & & receivedInvite . Header . ContainsKey ( "toGroupSipID" ) )
{
if ( receivedInvite . Header [ "isPTT" ] = = "false" )
{
sendRinging = false ;
OnError ( new ErrorEventArgs ( UserName , senderSipId , $"Will reject invite from {senderSipId} because it is a full duplex call" ) ) ;
}
else
{
// Call is PTT
// Check if is group call
string linxGroupID = null ;
if ( string . IsNullOrEmpty ( linxGroupID = receivedInvite . Header [ "toGroupSipID" ] ) )
{
// PTT group call
// Check if I am allready in group call
lock ( _lockerSipDialog )
{
if ( _linxGroupSipIDsInDialogWith . Contains ( linxGroupID ) )
{
sendRinging = false ;
OnError ( new ErrorEventArgs ( UserName , senderSipId , $"Will reject group invite from {senderSipId} because I'm already in group call with {linxGroupID}" ) ) ;
}
}
}
}
}
else
{
sendRinging = false ;
OnError ( new ErrorEventArgs ( UserName , senderSipId , $"Will reject invite from {senderSipId} because it is not propely formated" ) ) ;
}
return sendRinging ;
}
/// <summary>
/// Function that processes all the received Sip Message requests
/// </summary>
/// <param name="sipMessageRequest">The Sip Message request</param>
/// <param name="senderID">The ID of the sender</param>
protected virtual void ProcessReceivedSipMessage ( Request sipMessageRequest , string senderID )
{
// Handle messages when working with Asterisk server (set up by Andrei)
if ( sipMessageRequest . ContentType = = "text/plain;charset=UTF-8" )
{
// SMS received
if ( senderID = = "Unknown" )
{
// SMS confirmation from Sip server
if ( _smsConfirmationFromServer )
{
// Get sms destination and message
string smsKey = GetSmsKeyFromConfirmation ( sipMessageRequest . Body ) ;
lock ( _lockerSmsSet )
{
if ( _smsSentDict . ContainsKey ( smsKey ) )
{
// Set message as delivered or failed
_smsSentDict [ smsKey ] = GetDeliveredStatusFromConfirmation ( sipMessageRequest . Body ) ;
// Notify that we received a confirmation
Monitor . PulseAll ( _lockerSmsSet ) ;
}
}
}
}
else if ( sipMessageRequest . Header . Contains ( "Ais-Service" ) )
{
if ( sipMessageRequest . Header [ "Ais-Service" ] = = "mbus" )
{
string messageBody = sipMessageRequest . Body ;
if ( messageBody ! = null )
{
string [ ] tmp = messageBody . Split ( '#' ) ;
if ( tmp . Length > = 4 & & messageBody . Length = = Convert . ToInt32 ( tmp [ 1 ] ) )
{
switch ( tmp [ 3 ] )
{
case "138" :
// Emergency from Linx
if ( _previousEmergencySeqID ! = tmp [ 2 ] ) // Protection against multiple message error
{
_previousEmergencySeqID = tmp [ 2 ] ;
Linx . LinxEmergencyType type = Linx . LinxEmergencyType . REGULAR ;
if ( tmp [ 5 ] ! = null )
{
int emergencyType ;
if ( int . TryParse ( tmp [ 5 ] , out emergencyType ) )
{
if ( Enum . IsDefined ( typeof ( Linx . LinxEmergencyType ) , emergencyType ) )
{
type = ( Linx . LinxEmergencyType ) emergencyType ;
}
}
}
OnLinxEmergencyAlarmReceived ( new Linx . LinxEmergencyAlarmReceivedEventArgs ( senderID , tmp [ 2 ] , type ) ) ;
}
break ;
case "131" : // Periadically gps from Linx
case "231" : // Polled gps from Linx
int unixTime ;
double speed , latitude , longitude ;
if ( tmp . Length > = 9 )
{
// Protection against multiple message error
if ( _previousLocationSeqID ! = tmp [ 2 ] )
_previousLocationSeqID = tmp [ 2 ] ;
else
break ;
if ( double . TryParse ( tmp [ 6 ] , NumberStyles . AllowDecimalPoint , CultureInfo . InvariantCulture , out speed ) )
{
if ( int . TryParse ( tmp [ 5 ] , out unixTime ) )
{
if ( double . TryParse ( tmp [ 7 ] , NumberStyles . AllowDecimalPoint | NumberStyles . AllowLeadingWhite | NumberStyles . AllowLeadingSign , CultureInfo . InvariantCulture , out latitude ) )
{
if ( double . TryParse ( tmp [ 8 ] , NumberStyles . AllowDecimalPoint | NumberStyles . AllowLeadingWhite | NumberStyles . AllowLeadingSign , CultureInfo . InvariantCulture , out longitude ) )
{
if ( tmp [ 3 ] = = "131" )
{
// Periodically gps from Linx
OnGpsPeriodicallyReportReceived ( new GpsDataEventArgs (
senderID , latitude , longitude , speed , UnixTimeStampToDateTime ( unixTime ) ) ) ;
}
else if ( tmp [ 3 ] = = "231" )
{
OnGpsReportReceived ( new LinxGpsDataEventArgs (
senderID , tmp [ 2 ] , latitude , longitude , speed , UnixTimeStampToDateTime ( unixTime ) ) ) ;
}
}
}
}
}
}
break ;
case "130" : // Ars from Linx
if ( tmp . Length > = 6 )
{
if ( _previousArsSeqID ! = tmp [ 2 ] ) // Protection against multiple message error
{
_previousArsSeqID = tmp [ 2 ] ;
OnLinxArsReceived ( new Linx . ArsReceivedEventArgs ( senderID , tmp [ 2 ] , tmp [ 5 ] = = "ON" ? true : false ) ) ;
}
}
break ;
}
}
}
}
}
else
{
// Must be sms (:::seqID:::text:::)
string messageBody = sipMessageRequest . Body ;
if ( messageBody ! = null )
{
string [ ] tmp = messageBody . Split ( new string [ ] { ":::" } , StringSplitOptions . None ) ;
if ( tmp . Length = = 4 )
{
if ( _previousSmsSeqID ! = tmp [ 1 ] )
{
_previousSmsSeqID = tmp [ 1 ] ;
OnSipSmsReceived ( new SmsReceivedEventsArgs ( senderID , tmp [ 2 ] ) ) ;
}
}
}
}
}
}
#region Sms confirmation private helper functions
private string GetSmsKeyFromConfirmation ( string messageBody )
{
string toReturn = "" ;
int destinationSipID ;
string text ;
if ( int . TryParse ( GetDestinationIDFromConfirmation ( messageBody ) , out destinationSipID ) )
{
text = GetTextFromConfirmation ( messageBody ) ;
toReturn = destinationSipID . ToString ( ) + text . GetHashCode ( ) ;
}
return toReturn ;
}
private string GetTextFromConfirmation ( string source )
{
string start = "Your message - " ;
string end = " - to " ;
int startIndex , endIndex ;
string strToReturn = "" ;
if ( source . Contains ( start ) & & source . Contains ( end ) )
{
startIndex = source . IndexOf ( start ) + start . Length ;
endIndex = source . LastIndexOf ( end ) ;
strToReturn = source . Substring ( startIndex , endIndex - startIndex ) ;
}
return strToReturn ;
}
private string GetDestinationIDFromConfirmation ( string source )
{
string start = " - to " ;
string end = " has " ;
int startIndex , endIndex ;
string strToReturn = "" ;
if ( source . Contains ( start ) & & source . Contains ( end ) )
{
startIndex = source . LastIndexOf ( start ) + start . Length ;
endIndex = source . LastIndexOf ( end ) ;
strToReturn = source . Substring ( startIndex , endIndex - startIndex ) ;
}
return strToReturn ;
}
private bool GetDeliveredStatusFromConfirmation ( string source )
{
string lastWord = source . Substring ( source . LastIndexOf ( ' ' ) + 1 ) ;
if ( lastWord . Contains ( "delivered" ) )
return true ;
2024-06-20 16:03:35 +00:00
if ( lastWord . Contains ( "failed" ) )
2024-02-22 16:43:59 +00:00
return false ;
2024-06-20 16:03:35 +00:00
throw new ApplicationException ( "Error on parsing the sms confirmation from sip server" ) ;
2024-02-22 16:43:59 +00:00
}
#endregion
private void CreatingDialog ( string radioInDialogWith , SessionDescription receivedSDP , int localRTPport , bool initiatedByMe , string linxGroupID = null )
{
lock ( _lockerSipDialog )
{
if ( _IDdialogTuple . Count = = MaxNbOfDialogs )
throw new ApplicationException ( "Exceeded the maximum number of simultaneous dialogs: " + MaxNbOfDialogs ) ;
// Verific daca sunt deja in dialog cu userul
if ( ! _IDdialogTuple . ContainsKey ( radioInDialogWith ) )
{
// Extract ip and port where to send voice to simoco
IPAddress ipToSendAudio = null ;
int portToSendAudio ;
2024-06-20 16:03:35 +00:00
2024-02-22 16:43:59 +00:00
if ( IPAddress . TryParse ( receivedSDP . Connection . Address , out ipToSendAudio ) )
{
portToSendAudio = receivedSDP . Media [ 0 ] . Port ;
}
2024-06-20 16:03:35 +00:00
else
throw new ApplicationException ( "Canot determine ip where to send audio" ) ;
2024-02-22 16:43:59 +00:00
// Creez clientul de UDP conectat la portul pe care voi primi voce
UdpClient udpClient = new UdpClient ( localRTPport ) ;
//UdpClient udpClient = new UdpClient(new IPEndPoint(IPAddress.Parse(_localIPaddress), localRTPport));
// Create RTPSender (trebuie sa fie gata sa trimita mesaje catre simoco)
RTPSender2 rtpSender = new RTPSender2 (
udpClient ,
AudioBitrate ,
new IPEndPoint ( ipToSendAudio , portToSendAudio ) ,
int . Parse ( _sipClient . Username ) ,
int . Parse ( radioInDialogWith ) ,
initiatedByMe ,
TypeOfCall . FULL_DUPLEX ) ;
//
//_pttControlBlockingCollection = new BlockingCollection<PTTEventArgs>();
//Task.Factory.StartNew(() =>
//{
// ProcessPTTReceived();
//});
// Incep sa ascult RTP trimis de Simoco
RTPListener2 rtpListener = new RTPListenerLinx ( udpClient , initiatedByMe , _bufferMiliseconds , int . Parse ( radioInDialogWith ) , TypeOfCall . FULL_DUPLEX , linxGroupID ) ;
//_rtpListener.PTTControlReceived += PTTControlReceived;
rtpListener . VoiceReceived + = ReceivedVoice ;
rtpListener . ExceptionThrown + = ExceptionThrown ;
rtpListener . Start ( ) ;
// Timer hangtime
TimerWithTag < string > timerHangTime = null ;
if ( HangTimeDuration ! = - 1 )
{
timerHangTime = new TimerWithTag < string > ( HangTimeDuration * 1000 , radioInDialogWith ) ;
timerHangTime . AutoReset = false ;
timerHangTime . Elapsed + = timerHangTime_Elapsed ;
timerHangTime . Start ( ) ;
}
Tuple < UdpClient , RTPSender2 , RTPListener2 , TimerWithTag < string > > sipDialogTuple = Tuple . Create ( udpClient , rtpSender , rtpListener , timerHangTime ) ;
// Sunt in dialog cu statia
// Add to dictionary
_IDdialogTuple . Add ( radioInDialogWith , sipDialogTuple ) ;
_IDsInDialogWithList . Add ( radioInDialogWith ) ;
// Add to list of group calls
if ( linxGroupID ! = null )
_linxGroupSipIDsInDialogWith . Add ( linxGroupID ) ;
}
else
throw new SipClassException ( string . Format ( "Already in dialog with {0}" ,
radioInDialogWith ) ) ;
}
}
void timerHangTime_Elapsed ( object sender , System . Timers . ElapsedEventArgs e )
{
// End call
TimerWithTag < string > timer = ( TimerWithTag < string > ) sender ;
if ( _IDdialogTuple . ContainsKey ( timer . Tag ) )
{
Dialog dialogToClose = _sipClient . GetDialogWith ( timer . Tag ) ;
if ( dialogToClose ! = null )
{
string sipIDidDialogWith = timer . Tag ;
string linxGroupID = null ;
if ( ClosingDialog ( sipIDidDialogWith , _sipClient . Username , out linxGroupID ) )
{
// Send Bye
Task . Factory . StartNew ( ( dialogToCloseObj ) = >
{
_sipClient . Bye ( ( Dialog ) dialogToCloseObj ) ;
} , dialogToClose ) ;
// Fire event
OnDialogClosed ( new LinxDialogClosedEventArgs (
sipIDidDialogWith , _sipClient . Username , DialogClosedReason . TimerHangTimeExpired , linxGroupID ) ) ;
}
}
}
}
private bool ClosingDialog ( string idInDialogWith , string idWhoClosedTheDialog , out string linxGroupID )
{
linxGroupID = null ;
lock ( _lockerSipDialog )
{
if ( _IDdialogTuple . ContainsKey ( idInDialogWith ) )
{
Tuple < UdpClient , RTPSender2 , RTPListener2 , TimerWithTag < string > > dialogTuple = _IDdialogTuple [ idInDialogWith ] ;
// Opresc timerele (send ptt hearbeat, heartbeat query, etc) din sender
dialogTuple . Item2 . Stop ( ) ;
// Get the linxGroupID
linxGroupID = ( ( RTPListenerLinx ) dialogTuple . Item3 ) . LinxGroupID ;
// Opresc citirea pachetelor rtp
dialogTuple . Item3 . Stop ( ) ;
// Unregister form the lister events
dialogTuple . Item3 . VoiceReceived - = ReceivedVoice ;
// Close the udp port
dialogTuple . Item1 . Close ( ) ;
// dispose pentru _rtpListener
dialogTuple . Item3 . Dispose ( ) ;
// Opresc timerul hangtime
if ( dialogTuple . Item4 ! = null )
{
dialogTuple . Item4 . Elapsed - = timerHangTime_Elapsed ;
dialogTuple . Item4 . Stop ( ) ;
}
_IDdialogTuple . Remove ( idInDialogWith ) ;
_IDsInDialogWithList . Remove ( idInDialogWith ) ;
// remove from list of group dialogs
if ( linxGroupID ! = null )
_linxGroupSipIDsInDialogWith . Remove ( linxGroupID ) ;
return true ;
}
else
{
return false ;
}
}
}
private SipClient CreateSipClientClass ( string sipDomain , int sipDomainPort , string localIPaddress , int localSipPort , string userName , string password , int requestTimeout )
{
2024-06-20 16:03:35 +00:00
2024-02-22 16:43:59 +00:00
// Set up the master SIP class
2024-06-20 16:03:35 +00:00
SipClient sipClient = new SipClient ( sipDomain , sipDomainPort , Independentsoft . Sip . ProtocolType . Udp , userName , password ) ;
2024-02-22 16:43:59 +00:00
sipClient . LocalIPEndPoint = new IPEndPoint ( IPAddress . Parse ( localIPaddress ) , localSipPort ) ;
// Turn on logging
//sipClient.Logger = new Logger(AppDomain.CurrentDomain.BaseDirectory + "\\sipLog.txt");
sipClient . ReceiveRequest + = ProcessSipRequest ;
sipClient . ReceiveResponse + = ProcessSipResponse ;
sipClient . Timeout = requestTimeout ;
sipClient . Connect ( ) ;
_sipClassClosed = false ;
return sipClient ;
}
private void SendSipInvite ( string idToCall , bool isGroup )
{
lock ( _lockerSipDialog )
{
if ( ! _IDsentInviteDict . ContainsKey ( idToCall ) )
{
// Send invite to somewone
string sipServerIP = _sipClient . Domain ;
string sipID = _sipClient . Username ;
int rtpPort = ReturnAvailablePort ( ) ;
SessionDescription sdp = CreateSDP ( rtpPort ) ;
2024-06-20 16:03:35 +00:00
2024-02-22 16:43:59 +00:00
Invite inv = new Invite ( ) ;
inv . Uri = "sip:" + idToCall + "@" + sipServerIP ;
inv . From = new ContactInfo ( "sip:" + sipID . ToString ( ) + "@" + sipServerIP ) ;
inv . To = new ContactInfo ( "sip:" + idToCall + "@" + sipServerIP ) ;
inv . Contact = new Contact ( "sip:" + sipID . ToString ( ) + "@" + _sipClient . LocalIPEndPoint . ToString ( ) ) ;
inv . SessionDescription = sdp ;
// Add Adi headers
inv . Header . Add ( "fromSipID" , UserName ) ;
inv . Header . Add ( "fromUserID" , "" ) ;
inv . Header . Add ( "isPTT" , "true" ) ;
if ( isGroup )
{
inv . Header . Add ( "toGroupSipID" , idToCall ) ;
inv . Header . Add ( "toGroupID" , "" ) ;
inv . Header . Add ( "toSipID" , "" ) ;
inv . Header . Add ( "toUserID" , "" ) ;
inv . Header . Add ( "callType" , "group" ) ;
}
else
{
inv . Header . Add ( "toGroupSipID" , "" ) ;
inv . Header . Add ( "toGroupID" , "" ) ;
inv . Header . Add ( "toSipID" , idToCall ) ;
inv . Header . Add ( "toUserID" , "" ) ;
inv . Header . Add ( "callType" , "private" ) ;
}
//
// Add to dictionary
_IDsentInviteDict . Add ( idToCall , inv ) ;
_IDsCalledByMeList . Add ( idToCall ) ;
// Send invite request
Task . Factory . StartNew ( ( idToCallObj ) = >
{
string id = ( string ) idToCallObj ;
OnInviteSent ( new SipEventArgs ( id ) ) ;
try
{
_sipClient . SendRequest ( inv ) ;
}
catch ( Exception tEx )
{
// Timout exception
// Cancel the invite
Task t = null ;
SendCancelRequest ( id , t ) ;
// Fire event
OnError ( new ErrorEventArgs ( id , _sipClient . Username , tEx . Message ) ) ;
}
} , idToCall ) ;
}
}
}
private void SendSipRegister ( object registrationDataObj )
{
string sipServerIP = _sipClient . Domain ;
2024-06-20 16:03:35 +00:00
2024-02-22 16:43:59 +00:00
RegistrationData regData = ( RegistrationData ) registrationDataObj ;
string sipIDfrom = _sipClient . Username ;
2024-06-20 16:03:35 +00:00
string sipIDto = regData . SipID ;
2024-02-22 16:43:59 +00:00
int expiresValue = regData . Expires ;
bool isUnregisterRequest = ( expiresValue = = 0 ) ;
Register reg = new Register ( ) ;
reg . Uri = "sip:" + sipServerIP ;
2024-06-20 16:03:35 +00:00
reg . From = new ContactInfo ( sipIDfrom . ToString ( ) , $"sip:{sipIDfrom}@{sipServerIP}" ) ;
reg . To = new ContactInfo ( sipIDto . ToString ( ) , $"sip:{sipIDto}@{sipServerIP}" ) ;
2024-02-22 16:43:59 +00:00
reg . Contact = new Contact ( "sip:" + sipIDfrom . ToString ( ) + "@" + _sipClient . LocalIPEndPoint . ToString ( ) ) ;
reg . Expires = expiresValue ;
RegistrationStatus regStatus = _sipID_regTimer_regStatus_Dict [ sipIDto ] . Item2 ;
try
{
RequestResponse reqResp = _sipClient . SendRequest ( reg ) ;
if ( reqResp . Response . StatusCode = = 200 ) //OK
{
if ( isUnregisterRequest )
{
regStatus = RegistrationStatus . UnregisteredAtRequest ;
}
else
{
regStatus = RegistrationStatus . Registered ;
}
}
else if ( reqResp . Response . StatusCode = = 403 ) // 403 = Forbidden
{
regStatus = RegistrationStatus . WrongCredentials ;
}
else
{
regStatus = RegistrationStatus . NotRegistered ;
// to do - delete this
Console . WriteLine ( $"Received response: {reqResp.Response.StatusCode} to sip registration request" ) ;
}
}
catch ( SocketException socketEx )
{
regStatus = RegistrationStatus . SocketError ;
}
catch ( Independentsoft . Sip . TimeoutException )
{
regStatus = RegistrationStatus . RequestTimeout ;
}
catch ( Exception ex )
{
regStatus = RegistrationStatus . NotRegistered ;
// to do - delete this
Console . WriteLine ( "Exception when sending registration to Sip server\n" + ex . ToString ( ) ) ;
}
if ( regStatus ! = _sipID_regTimer_regStatus_Dict [ sipIDto ] . Item2 )
{
if ( regStatus ! = RegistrationStatus . UnregisteredAtRequest )
{
// Just change the registration status
System . Timers . Timer currentTimer = _sipID_regTimer_regStatus_Dict [ sipIDto ] . Item1 ;
_sipID_regTimer_regStatus_Dict [ sipIDto ] = new Tuple < System . Timers . Timer , RegistrationStatus > ( currentTimer , regStatus ) ;
}
else
{
// Unregistered - Remove from dictionary
_sipID_regTimer_regStatus_Dict . Remove ( sipIDto ) ;
_IDsregisteredList . Remove ( sipIDto ) ;
}
//OnRegistrationStateChanged(sipIDto, regStatus);
OnRegistrationStateChanged ( new RegistrationStateChangedEventArgs ( sipIDto , regStatus ) ) ;
}
}
private SessionDescription CreateSDP ( int rtpPort )
{
SessionDescription sdpSession = new SessionDescription ( ) ;
// Origin (Owner) - 0=<userName> <sessionId> <version> <network type> <address type>
// <sessionId> must be unique
int sessionID = _rand . Next ( ) ;
// <version> - must be increased when a modification is made to the session data.
Owner owner = new Owner ( _sipClient . Username , sessionID , 18299 , _sipClient . LocalIPEndPoint . Address . ToString ( ) ) ;
Connection connection = new Connection ( _sipClient . LocalIPEndPoint . Address . ToString ( ) ) ;
sdpSession . Owner = owner ;
sdpSession . Name = "_" ;
sdpSession . Connection = connection ;
sdpSession . Time . Add ( new Time ( 0 , 0 ) ) ;
// m=<media> <port> <transport> <fmt list>
// <port> - 1024 to 65535 for UDP
// - must be even for RTP
Media media1 = new Media ( "audio" , rtpPort , "RTP/AVP" ) ;
media1 . MediaFormats . Add ( "8" ) ;
media1 . Attributes . Add ( "rtpmap" , "8 PCMA/8000" ) ;
sdpSession . Media . Add ( media1 ) ;
return sdpSession ;
}
private void StartRegistrationTimer ( )
{
// Send instant registration request
Task . Factory . StartNew ( ( state ) = >
{
SendSipRegister ( state ) ;
} , _registrationData ) ;
2024-06-20 16:03:35 +00:00
2024-02-22 16:43:59 +00:00
// Start registration timer
_sipID_regTimer_regStatus_Dict [ UserName ] . Item1 . Start ( ) ;
}
private void _registrationTimer_Elapsed ( object sender , System . Timers . ElapsedEventArgs e )
{
SendSipRegister ( _registrationData ) ;
}
#endregion
#region Private Helper Methods
private void _socketIOClass_SmsReceived ( object sender , SmsReceivedEventArgs e )
{
SmsInfo smsInfo = e . SmsInfo ;
#if DEBUG
Console . WriteLine ( $"Socket.IO sms received by sip {UserName}: {smsInfo}" ) ;
#endif
// Fire event
OnSocketIoSmsReceived ( new SocketIOSmsEventArgs ( smsInfo . SeqID , smsInfo . FromSipID , smsInfo . ToSipID , smsInfo . Message , smsInfo . IsGroup ) ) ;
}
private void _socketIOClass_SmsAckReceived ( object sender , SmsAckReceivedEventArgs e )
{
SmsAckInfo smsAckInfo = e . SmsAckInfo ;
#if DEBUG
Console . WriteLine ( $"Socket.IO sms ack received by sip {UserName}: {smsAckInfo}" ) ;
#endif
// Fire event
OnSocketIoSmsAckReceived ( new SocketIoSmsAckEventArgs ( smsAckInfo . SeqID , smsAckInfo . AckBySipID . ToString ( ) ) ) ;
}
private int ReturnAvailablePort ( )
{
lock ( _lockerRtpPort )
{
int rtpPort = MinRtpPortNumber ;
while (
( IsPortAllreadyInUse ( rtpPort ) | | PortNumerProposedInSentInvites ( rtpPort ) )
& & rtpPort < MaxRtpPortNumber )
{
rtpPort + = 2 ;
}
2024-06-20 16:03:35 +00:00
2024-02-22 16:43:59 +00:00
if ( rtpPort < MaxRtpPortNumber )
return rtpPort ;
2024-06-20 16:03:35 +00:00
throw new SipClassException (
2024-02-22 16:43:59 +00:00
string . Format ( "Nu gasesc port liber in range-ul {0} - {1}" , MinRtpPortNumber , MaxRtpPortNumber ) ) ;
}
}
private bool PortNumerProposedInSentInvites ( int portNb )
{
Invite inv = _IDsentInviteDict . Values . FirstOrDefault ( ( invite ) = >
{
return invite . SessionDescription . Media [ 0 ] . Port = = portNb ;
} ) ;
2024-06-20 16:03:35 +00:00
return ( inv ! = null ) ;
2024-02-22 16:43:59 +00:00
}
private bool IsPortAllreadyInUse ( int portNumber )
{
return ( from p in System . Net . NetworkInformation . IPGlobalProperties . GetIPGlobalProperties ( ) . GetActiveUdpListeners ( )
where p . Port = = portNumber
select p ) . Count ( ) = = 1 ;
}
/// <summary>
/// Checks if an integer can be used as a rtp port
/// </summary>
/// <param name="rtpPort">The integer to be cecked</param>
/// <returns>True if is a valid rtp port, else false</returns>
public static bool ValidRtpPort ( int rtpPort )
{
if ( rtpPort < 1024 | | rtpPort > 65534 )
return false ;
2024-06-20 16:03:35 +00:00
if ( rtpPort % 2 = = 1 )
2024-02-22 16:43:59 +00:00
return false ;
2024-06-20 16:03:35 +00:00
return true ;
2024-02-22 16:43:59 +00:00
}
/// <summary>
/// Determines the most appropriate local end point to contact the provided remote end point.
/// Testing shows this method takes on average 1.6ms to return.
/// </summary>
/// <param name="remoteIPEndPoint">The remote end point</param>
/// <returns>The selected local end point</returns>
private static IPEndPoint BestLocalEndPoint ( IPEndPoint remoteIPEndPoint )
{
Socket testSocket = new Socket ( remoteIPEndPoint . AddressFamily , SocketType . Dgram , System . Net . Sockets . ProtocolType . Udp ) ;
testSocket . Connect ( remoteIPEndPoint ) ;
return ( IPEndPoint ) testSocket . LocalEndPoint ;
}
private bool IsInSameSubnet ( IPAddress address2 , IPAddress address , IPAddress subnetMask )
{
IPAddress network1 = GetNetworkAddress ( address , subnetMask ) ;
IPAddress network2 = GetNetworkAddress ( address2 , subnetMask ) ;
return network1 . Equals ( network2 ) ;
}
private IPAddress GetNetworkAddress ( IPAddress address , IPAddress subnetMask )
{
byte [ ] ipAdressBytes = address . GetAddressBytes ( ) ;
byte [ ] subnetMaskBytes = subnetMask . GetAddressBytes ( ) ;
if ( ipAdressBytes . Length ! = subnetMaskBytes . Length )
throw new ArgumentException ( "Lengths of IP address and subnet mask do not match." ) ;
byte [ ] broadcastAddress = new byte [ ipAdressBytes . Length ] ;
for ( int i = 0 ; i < broadcastAddress . Length ; i + + )
{
broadcastAddress [ i ] = ( byte ) ( ipAdressBytes [ i ] & ( subnetMaskBytes [ i ] ) ) ;
}
return new IPAddress ( broadcastAddress ) ;
}
private string AddMsgLenForMBus ( string textToSend )
{
String cmdok = textToSend ;
Int32 tmp = cmdok . Length + 1 ;
tmp + = tmp . ToString ( ) . Length ;
String TMPcmdok = "#" + tmp . ToString ( ) + cmdok ;
if ( tmp ! = TMPcmdok . Length ) cmdok = "#" + TMPcmdok . Length + cmdok ;
else cmdok = TMPcmdok ;
return cmdok ;
}
private DateTime UnixTimeStampToDateTime ( double unixTimeStamp )
{
// Unix timestamp is seconds past epoch
System . DateTime dtDateTime = new DateTime ( 1970 , 1 , 1 , 0 , 0 , 0 , 0 ) ;
dtDateTime = dtDateTime . AddSeconds ( unixTimeStamp ) ;
return dtDateTime ;
}
private double DateTimeToUnixTime ( DateTime utcTime )
{
System . DateTime baseTime = new DateTime ( 1970 , 1 , 1 , 0 , 0 , 0 , 0 ) ;
return ( utcTime - baseTime ) . TotalSeconds ;
}
#endregion
#region Events
/// <summary>
/// Occurs when you send an invite to a sip id
/// </summary>
public event EventHandler < SipEventArgs > InviteSent ;
/// <summary>
/// Occurs when you cancel a sent invite
/// </summary>
public event EventHandler < SipEventArgs > InviteSentCanceled ;
/// <summary>
/// Occurs when you receive an invite
/// </summary>
public event EventHandler < InviteReceivedArgs > InviteReceived ;
/// <summary>
/// Occurs when an invite is cancelled before you had time to accept or reject it.
/// </summary>
public event EventHandler < SipEventArgs > InviteReceivedCanceled ;
/// <summary>
/// Occurs when you decline an invite from another user
/// </summary>
public event EventHandler < SipEventArgs > InviteReceivedDeclined ;
/// <summary>
/// An error occured while attempting to establish a voice session
/// </summary>
public event EventHandler < ErrorEventArgs > ErrorOnCreatingDialog ;
/// <summary>
/// Occurs when a voice session (a dialog) has been established
/// </summary>
public event EventHandler < LinxDialogCreatedEventArgs > DialogCreated ;
/// <summary>
/// Occurs when a voice session (a dialog) is closed
/// </summary>
public event EventHandler < LinxDialogClosedEventArgs > DialogClosed ;
/// <summary>
/// Ocurs when a voice buffer is received from a sip id
/// </summary>
public event EventHandler < LinxAudioEventArgs > VoiceReceived ;
/// <summary>
/// Occurs when you receive an sms as a Sip message
/// </summary>
public event EventHandler < SmsReceivedEventsArgs > SipSmsReceived ;
/// <summary>
/// Occurs when the sip registration status changes
/// </summary>
public event EventHandler < RegistrationStateChangedEventArgs > RegistrationStateChanged ;
/// <summary>
/// Occurs when a GPS report is received from a Linx device
/// </summary>
public event EventHandler < LinxGpsDataEventArgs > GpsReportReceived ;
/// <summary>
/// Occurs when a periodically GPS report is received
/// </summary>
public event EventHandler < GpsDataEventArgs > GpsPeriodicallyReportReceived ;
/// <summary>
/// Occurs when an emergency alarm is received from a Linx device
/// </summary>
public event EventHandler < Linx . LinxEmergencyAlarmReceivedEventArgs > EmergencyAlarmReceived ;
/// <summary>
/// Occurs when an Ars command is received from a Linx device
/// </summary>
protected event EventHandler < Linx . ArsReceivedEventArgs > LinxArsReceived ;
/// <summary>
/// Occurs when an sms is received on socketIO
/// </summary>
public event EventHandler < SocketIOSmsEventArgs > SocketIOSmsReceived ;
/// <summary>
/// Occurs when an sms ack is received on socketIO
/// </summary>
public event EventHandler < SocketIoSmsAckEventArgs > SocketIOSmsAckReceived ;
/// <summary>
/// Occurs when hangtime is ended
/// </summary>
public event EventHandler < HangtimeEndedEventArgs > HangtimeEnded ;
private void OnSocketIoSmsReceived ( SocketIOSmsEventArgs e )
{
// New pattern for triggering events
SocketIOSmsReceived ? . Invoke ( this , e ) ;
}
private void OnSocketIoSmsAckReceived ( SocketIoSmsAckEventArgs e )
{
SocketIOSmsAckReceived ? . Invoke ( this , e ) ;
}
private void OnInviteSent ( SipEventArgs e )
{
EventHandler < SipEventArgs > handler = InviteSent ;
if ( handler ! = null )
handler ( this , e ) ;
}
private void OnInviteSentCanceled ( SipEventArgs e )
{
EventHandler < SipEventArgs > handler = InviteSentCanceled ;
if ( handler ! = null )
handler ( this , e ) ;
}
/// <summary>
/// This is the method that raises the ErrorOnCreatingDialog event
/// </summary>
/// <param name="e">Data for the ErrorOnCreatingDialog event</param>
/// <remarks>Do not forget to call the base version when overrinding,
/// <para>otherwize the ErrorOnCreatingDialog event will not fire</para>
/// </remarks>
protected virtual void OnError ( ErrorEventArgs e )
{
EventHandler < ErrorEventArgs > handler = ErrorOnCreatingDialog ;
if ( handler ! = null )
handler ( this , e ) ;
}
private void OnInviteReceived ( InviteReceivedArgs e )
{
EventHandler < InviteReceivedArgs > handler = InviteReceived ;
if ( handler ! = null )
handler ( this , e ) ;
}
private void OnInviteReceivedCanceled ( SipEventArgs e )
{
EventHandler < SipEventArgs > handler = InviteReceivedCanceled ;
if ( handler ! = null )
handler ( this , e ) ;
}
private void OnInviteReceivedDeclined ( SipEventArgs e )
{
EventHandler < SipEventArgs > handler = InviteReceivedDeclined ;
if ( handler ! = null )
handler ( this , e ) ;
}
/// <summary>
/// This is the method that raises the DialogCreated event
/// </summary>
/// <param name="e">Data for the DialogCreatedEvent</param>
/// <remarks>Do not forget to call the base version when overrinding,
/// <para>otherwize the DialogCreated Event will not fire</para>
/// </remarks>
protected virtual void OnDialogCreated ( LinxDialogCreatedEventArgs e )
{
EventHandler < LinxDialogCreatedEventArgs > handler = DialogCreated ;
if ( handler ! = null )
handler ( this , e ) ;
}
/// <summary>
/// This is the method that raises the DialogClosed event
/// </summary>
/// <param name="e">Data for the DialogClosed event</param>
/// <remarks>Do not forget to call the base version when overrinding,
/// <para>otherwize the DialogClosed Event will not fire</para>
/// </remarks>
protected virtual void OnDialogClosed ( LinxDialogClosedEventArgs e )
{
EventHandler < LinxDialogClosedEventArgs > handler = DialogClosed ;
if ( handler ! = null )
{
handler ( this , e ) ;
}
}
/// <summary>
/// This is the method that raises the VoiceReceived event
/// </summary>
/// <param name="e">Data for the VoiceReceived event</param>
/// <remarks>Do not forget to call the base version when overrinding,
/// <para>otherwise the VoiceReceived Event will not fire</para>
/// </remarks>
protected virtual void OnVoiceReceived ( LinxAudioEventArgs e )
{
EventHandler < LinxAudioEventArgs > handler = VoiceReceived ;
if ( handler ! = null )
handler ( this , e ) ;
}
private void OnRegistrationStateChanged ( RegistrationStateChangedEventArgs e )
{
EventHandler < RegistrationStateChangedEventArgs > handler = RegistrationStateChanged ;
if ( handler ! = null )
handler ( this , e ) ;
}
private void OnSipSmsReceived ( SmsReceivedEventsArgs e )
{
EventHandler < SmsReceivedEventsArgs > handler = SipSmsReceived ;
if ( handler ! = null )
handler ( this , e ) ;
}
private void OnGpsPeriodicallyReportReceived ( GpsDataEventArgs e )
{
EventHandler < GpsDataEventArgs > handler = GpsPeriodicallyReportReceived ;
if ( handler ! = null )
handler ( this , e ) ;
}
private void OnLinxEmergencyAlarmReceived ( Linx . LinxEmergencyAlarmReceivedEventArgs e )
{
EventHandler < Linx . LinxEmergencyAlarmReceivedEventArgs > handler = EmergencyAlarmReceived ;
if ( handler ! = null )
handler ( this , e ) ;
}
private void OnGpsReportReceived ( LinxGpsDataEventArgs e )
{
EventHandler < LinxGpsDataEventArgs > handler = GpsReportReceived ;
if ( handler ! = null )
handler ( this , e ) ;
}
private void OnLinxArsReceived ( Linx . ArsReceivedEventArgs e )
{
EventHandler < Linx . ArsReceivedEventArgs > handler = LinxArsReceived ;
if ( handler ! = null )
handler ( this , e ) ;
}
#endregion
}
}