using Independentsoft.Sip;
using Independentsoft.Sip.Methods;
using Independentsoft.Sip.Responses;
using Independentsoft.Sip.Sdp;
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.Threading;
using System.Threading.Tasks;
namespace SipComponent
{
///
/// Class used to make SD talk to Linx using Adi's protocol, version 3
/// Accepts more than one sip call at a time
/// Does not include a socket.IO component
///
class SipClientClass3
{
#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;
///
/// Nr de secunde cat este mentinut call-ul cand nu se transmite voce
///
private int _hangTimeDuration = -1;
private int _bufferMiliseconds;
private string _localIPaddress;
private Random _rand = new Random();
private bool _sipClassClosed = false;
///
/// Gets or sets a value indicating if the Asterisk server confirms sms
/// Default value is false
///
protected bool _smsConfirmationFromServer = false;
private Dictionary _IDsentInviteDict = new Dictionary();
private Dictionary _IDreceivedInviteDict = new Dictionary();
private Dictionary>> _IDdialogTuple = new Dictionary>>();
private Dictionary _smsSentDict = new Dictionary();
private Dictionary> _sipID_regTimer_regStatus_Dict =
new Dictionary>();
private Dictionary>> _ID_pingBytest_timer_dict = new Dictionary>>();
private Dictionary _simocoID_emergencyStatusReport_dict = new Dictionary();
private object _lockerSmsSet = new object();
private List _IDsCalledByMeList = new List();
private List _IDsCallingMeList = new List();
private List _IDsInDialogWithList = new List();
private List _IDsregisteredList = new List();
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
HashSet _linxGroupSipIDsInDialogWith = new HashSet();
#endregion
#region Public Properties
///
/// Gets or sets the minimum port number in the port range used for RTP voice transmission
///
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));
}
}
///
/// Gets or sets the maximum port number in the port range used for RTP voice transmission
///
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));
}
}
///
/// Gets the sip ID
///
public string UserName
{
get { return _sipClient.Username; }
}
///
/// Gets or sets the maximum number of simultaneous dialogs that the SipClientClass accepts
/// The default is 3
///
public int MaxNbOfDialogs
{
get { return _maxNbOfDialogs; }
set
{
_maxNbOfDialogs = value;
}
}
///
/// Gets or sets the number of seconds the call is maintained when no voice is transmited
/// Value is in seconds
/// Default value is -1, call is maintained indefinitely
///
public int HangTimeDuration
{
get { return _hangTimeDuration; }
set
{
if (value > 0)
_hangTimeDuration = value;
}
}
///
/// Gets or sets a value indicating the nb of seconds to wait
/// for a sms confirmation from sip server
/// Default value is 3
///
public int SecondsToWaitForSmsConfirmation
{
get { return _secondsToWaitForSmsConfirmation; }
set
{
if (value > 0 && value < 10)
_secondsToWaitForSmsConfirmation = value;
}
}
///
/// Audio BitRate, default value is 16
///
public int AudioBitrate
{
get { return _audioBitRate; }
set { _audioBitRate = value; }
}
///
/// Gets a readonly collection of sip ids that you sent Invite for
///
public ReadOnlyCollection SipIDsCalledByMe
{
get { return new ReadOnlyCollection(_IDsCalledByMeList); }
}
///
/// Gets a readonly collection of sip ids that sent you Invite's
///
public ReadOnlyCollection SipIDsCallingMe
{
get { return new ReadOnlyCollection(_IDsCallingMeList); }
}
///
/// Gets a readonly collection of sip ids that you are in dialog with
///
public ReadOnlyCollection SipIDsInDialogWith
{
get { return new ReadOnlyCollection(_IDsInDialogWithList); }
}
///
/// Gets a readonly collection of sip ids that you are registered to
/// This means your sip id and the group ids that you are listening to
///
public ReadOnlyCollection SipIDsRegisteredTo
{
get
{
return new ReadOnlyCollection(_IDsregisteredList);
}
}
///
/// Gets or sets a value indicating if the Asterisk sends sms confirmations on delivery
/// Default value is false
///
public bool SmsConfirmationFromAsterisk
{
get { return _smsConfirmationFromServer; }
set { _smsConfirmationFromServer = true; }
}
#endregion
#region Constructor
// constructor
///
/// Constructor for the SipClientClass
///
/// domain name, name or IP address of sip server
/// port number of the sip server
/// port number of the local computer used for sip protocol
/// user name on the sip server
/// password on the sip server
/// interval to send sip registration requests. Value is in seconds
/// Miliseconds for the buffer that stores the received voice packets
/// Number of ms to wait before the sip request times out
/// Port number use for socket.IO
/// Local Ip adress. If not specified, the class will search for a local ip on the same network with the sip server ip
public SipClientClass3(string sipDomain, int sipDomainPort, int localSipPort, string userName, string password, int registrationInterval, int bufferMiliseconds,
int requestTimeout, int socketIOport, 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;
_sipDomainPort = sipDomainPort;
_sipClient = CreateSipClientClass(sipDomain, _sipDomainPort, _localIPaddress, localSipPort, userName, password, requestTimeout);
_bufferMiliseconds = bufferMiliseconds;
// Registration Timer
_registrationInterval = registrationInterval;
_registrationData = new RegistrationData(userName, _registrationInterval + 2);
System.Timers.Timer registrationTimer = new System.Timers.Timer();
// Set up the registration timer
registrationTimer.Interval = _registrationInterval * 1000;
registrationTimer.Elapsed += _registrationTimer_Elapsed;
_sipID_regTimer_regStatus_Dict.Add(userName,
new Tuple(registrationTimer, RegistrationStatus.RegistrationNotStarted
));
_IDsregisteredList.Add(userName);
StartRegistrationTimer();
}
#endregion
#region Public Methods
///
/// Sends a Sip Invite command to an user
///
/// The sip id of the user
public void Invite(string idToInvite)
{
if (_sipClassClosed)
throw new ObjectDisposedException("SipClientClass");
if (idToInvite == null)
throw new ArgumentNullException("idToInvite");
lock (_lockerSipDialog)
{
if (!_IDsentInviteDict.ContainsKey(idToInvite) && !_IDreceivedInviteDict.ContainsKey(idToInvite)
&& !_IDdialogTuple.ContainsKey(idToInvite))
{
SendSipInvite(idToInvite, false);
}
}
}
///
/// Sends a Sip Invite command to a Simoco Group
///
/// The sip id of the group
public void InviteGroup(string groupIDtoInvite)
{
if (_sipClassClosed)
throw new ObjectDisposedException("SipClientClass");
if (groupIDtoInvite == null)
throw new ArgumentNullException("groupIDtoInvite");
// 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"));
}
}
///
/// Cancels an Invite sent to an user
///
/// The sip id of the user
public void CancelInvite(string idToCancel)
{
if (_sipClassClosed)
throw new ObjectDisposedException("SipClientClass");
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;
});
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);
}
// Remove from dict
_IDsentInviteDict.Remove(idToCancel);
_IDsCalledByMeList.Remove(idToCancel);
}
return sendingCancelTask;
}
///
/// Accepts an Invite from an user
///
/// The sip ID of the user
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] = "";
// 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));
}
}
///
/// Closes a sip dialog (voice session) with an user
///
/// The sip id of the user
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;
}
///
/// Declines a received Invite from an user
///
/// The sip ID of the user
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));
}
///
/// Sends a voice buffer in a specified format to the user in dialog with
///
/// The sip ID of the user
/// The audio buffer
/// The length of the buffer
/// The audio format of the buffer
public virtual void SendAudio(string idToSendVoice, byte[] audioBuffer, int bufferLength, AudioFormat format)
{
if (_sipClassClosed)
throw new ObjectDisposedException("SipClientClass");
lock (_lockerSipDialog)
{
if (_IDdialogTuple.ContainsKey(idToSendVoice))
{
_IDdialogTuple[idToSendVoice].Item2.SendAudio(audioBuffer, bufferLength, format);
}
}
}
///
/// Method used to send Gps Request to a Linx device
///
/// The sip id to send the gps request
/// The sequence id
protected void SendGpsRequest(string idToRequestGps, string seqID)
{
if (_sipClassClosed)
throw new ObjectDisposedException("SipClientClass");
if (idToRequestGps != null)
{
// 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(() =>
{
try
{
_sipClient.SendRequest(pollRequestForLinx);
}
catch (Exception)
{
; // Probably timeout exception, do not do anything
}
});
}
else
throw new ArgumentNullException("idToRequestGps");
}
///
/// Method used to acknowledge an emergency alarm sent by a Linx device
///
/// The sip id of the Linx device
protected void AcknowledgeLinxEmergencyAlarm(string linxID)
{
if (_sipClassClosed)
throw new ObjectDisposedException("SipClientClass");
if (linxID != null)
{
// 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(() =>
{
try
{
_sipClient.SendRequest(sipMessage);
}
catch (Exception)
{
// Do nothing
}
});
}
else
throw new ArgumentNullException("linxID");
}
///
/// Method used to send an sms to sip id using the sip protocol
/// This method does not block the calling thread while waiting for the confirmation
///
/// The sip id where to send the sms
/// The sms text
/// True if the sms was received, else returns false
public async Task SendSmsAsync(string idToSendSMS, string text)
{
if (_sipClassClosed)
throw new ObjectDisposedException("SipClientClass");
if (idToSendSMS != null && text != null)
{
bool sendSipMessage = true;
string unconfirmedSmsKey = null;
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;
}
}
}
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(SmsConfirmedBySipServer, unconfirmedSmsKey);
}
else if (idToSendSMS == null)
throw new ArgumentNullException("idToSendSMS");
else
throw new ArgumentNullException("text");
}
private Message GenerateSipMessage(string destinationID, string senderID, string sipServer,
int sipServerPort, string text)
{
Message sipMessage = new Message();
sipMessage.Uri = "sip:" + destinationID + "@" + sipServer + ":" + sipServerPort;
sipMessage.From = new ContactInfo(string.Format("sip:{0}@{1}", senderID, sipServer));
sipMessage.To = new ContactInfo(string.Format("sip:{0}@{1}", destinationID, sipServer));
sipMessage.ContentType = "text/plain;charset=UTF-8";
sipMessage.Body = text;
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;
}
///
/// Mehod used to check if you are in dialog with a sip id
///
/// The sip id
/// True if you are in dialog, else returns false
public bool InDialogWith(string sipID)
{
if (_sipClassClosed)
throw new ObjectDisposedException("SipClientClass");
lock (_lockerSipDialog)
{
return _IDdialogTuple.ContainsKey(sipID);
}
}
///
/// Stops registration by sending unregister request to the sip server
/// Releases all the used resources
/// If true, method returns before all the resources are released
///
public virtual void Stop(bool async = true)
{
if (!_sipClassClosed)
{
_sipClassClosed = true;
List sipIDs = new List();
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();
});
if (!async)
t.Wait();
}
}
///
/// Stops registration by sending unregister request to the sip server
///
public void StopRegisterToSipServer()
{
// Stop registering to groups
List idsToUnregisterFrom = new List(_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);
_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;
}
}
///
/// Method used to decide if the app is ringing or is sending Busy response to sender
/// Default behaviour is to ring (sends Ringing response)
///
/// The received invite
/// The sender of the invite
/// True to send Ringing response, false to send Busy Here
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;
}
///
/// Function that processes all the received Sip Message requests
///
/// The Sip Message request
/// The ID of the sender
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;
else if (lastWord.Contains("failed"))
return false;
else
throw new ApplicationException("Error on parsing the sms confirmation from sip server");
}
#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;
if (IPAddress.TryParse(receivedSDP.Connection.Address, out ipToSendAudio))
{
portToSendAudio = receivedSDP.Media[0].Port;
}
else throw new ApplicationException("Canot determine ip where to send audio");
// 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();
//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 timerHangTime = null;
if (HangTimeDuration != -1)
{
timerHangTime = new TimerWithTag(HangTimeDuration * 1000, radioInDialogWith);
timerHangTime.AutoReset = false;
timerHangTime.Elapsed += timerHangTime_Elapsed;
timerHangTime.Start();
}
Tuple> 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 timer = (TimerWithTag)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> 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)
{
SipClient sipClient;
// Set up the master SIP class
sipClient = new SipClient(sipDomain, sipDomainPort, Independentsoft.Sip.ProtocolType.Udp, userName, password);
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);
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;
RegistrationData regData = (RegistrationData)registrationDataObj;
string sipIDfrom = _sipClient.Username;
string sipIDto = regData.SipID;
int expiresValue = regData.Expires;
bool isUnregisterRequest = (expiresValue == 0);
Register reg = new Register();
reg.Uri = "sip:" + sipServerIP;
reg.From = new ContactInfo(
sipIDfrom.ToString(),
"sip:" + sipIDfrom + "@" + sipServerIP);
reg.To = new ContactInfo(
sipIDto.ToString(),
"sip:" + sipIDto.ToString() + "@" + sipServerIP);
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(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=
// must be unique
int sessionID = _rand.Next();
// - 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=
// - 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);
// 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 int ReturnAvailablePort()
{
lock (_lockerRtpPort)
{
int rtpPort = MinRtpPortNumber;
while (
(IsPortAllreadyInUse(rtpPort) || PortNumerProposedInSentInvites(rtpPort))
&& rtpPort < MaxRtpPortNumber)
{
rtpPort += 2;
}
if (rtpPort < MaxRtpPortNumber)
return rtpPort;
else
throw new SipClassException(
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;
});
if (inv != null)
return true;
else
return false;
}
private bool IsPortAllreadyInUse(int portNumber)
{
return (from p in System.Net.NetworkInformation.IPGlobalProperties.GetIPGlobalProperties().GetActiveUdpListeners()
where p.Port == portNumber
select p).Count() == 1;
}
///
/// Checks if an integer can be used as a rtp port
///
/// The integer to be cecked
/// True if is a valid rtp port, else false
public static bool ValidRtpPort(int rtpPort)
{
if (rtpPort < 1024 || rtpPort > 65534)
{
return false;
}
else if (rtpPort % 2 == 1)
{
return false;
}
else
return true;
}
///
/// 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.
///
/// The remote end point
/// The selected local end point
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
///
/// Occurs when you send an invite to a sip id
///
public event EventHandler InviteSent;
///
/// Occurs when you cancel a sent invite
///
public event EventHandler InviteSentCanceled;
///
/// Occurs when you receive an invite
///
public event EventHandler InviteReceived;
///
/// Occurs when an invite is cancelled before you had time to accept or reject it.
///
public event EventHandler InviteReceivedCanceled;
///
/// Occurs when you decline an invite from another user
///
public event EventHandler InviteReceivedDeclined;
///
/// An error occured while attempting to establish a voice session
///
public event EventHandler ErrorOnCreatingDialog;
///
/// Occurs when a voice session (a dialog) has been established
///
public event EventHandler DialogCreated;
///
/// Occurs when a voice session (a dialog) is closed
///
public event EventHandler DialogClosed;
///
/// Ocurs when a voice buffer is received from a sip id
///
public event EventHandler VoiceReceived;
///
/// Occurs when you receive an sms as a Sip message
///
public event EventHandler SipSmsReceived;
///
/// Occurs when the sip registration status changes
///
public event EventHandler RegistrationStateChanged;
///
/// Occurs when a GPS report is received from a Linx device
///
public event EventHandler GpsReportReceived;
///
/// Occurs when a periodically GPS report is received
///
public event EventHandler GpsPeriodicallyReportReceived;
///
/// Occurs when an emergency alarm is received from a Linx device
///
public event EventHandler EmergencyAlarmReceived;
///
/// Occurs when an Ars command is received from a Linx device
///
protected event EventHandler LinxArsReceivedOnSip;
///
/// Occurs when hangtime is ended
///
public event EventHandler HangtimeEnded;
private void OnInviteSent(SipEventArgs e)
{
EventHandler handler = InviteSent;
if (handler != null)
handler(this, e);
}
private void OnInviteSentCanceled(SipEventArgs e)
{
EventHandler handler = InviteSentCanceled;
if (handler != null)
handler(this, e);
}
///
/// This is the method that raises the ErrorOnCreatingDialog event
///
/// Data for the ErrorOnCreatingDialog event
/// Do not forget to call the base version when overrinding,
/// otherwize the ErrorOnCreatingDialog event will not fire
///
protected virtual void OnError(ErrorEventArgs e)
{
EventHandler handler = ErrorOnCreatingDialog;
if (handler != null)
handler(this, e);
}
private void OnInviteReceived(InviteReceivedArgs e)
{
EventHandler handler = InviteReceived;
if (handler != null)
handler(this, e);
}
private void OnInviteReceivedCanceled(SipEventArgs e)
{
EventHandler handler = InviteReceivedCanceled;
if (handler != null)
handler(this, e);
}
private void OnInviteReceivedDeclined(SipEventArgs e)
{
EventHandler handler = InviteReceivedDeclined;
if (handler != null)
handler(this, e);
}
///
/// This is the method that raises the DialogCreated event
///
/// Data for the DialogCreatedEvent
/// Do not forget to call the base version when overrinding,
/// otherwize the DialogCreated Event will not fire
///
protected virtual void OnDialogCreated(LinxDialogCreatedEventArgs e)
{
EventHandler handler = DialogCreated;
if (handler != null)
handler(this, e);
}
///
/// This is the method that raises the DialogClosed event
///
/// Data for the DialogClosed event
/// Do not forget to call the base version when overrinding,
/// otherwize the DialogClosed Event will not fire
///
protected virtual void OnDialogClosed(LinxDialogClosedEventArgs e)
{
EventHandler handler = DialogClosed;
if (handler != null)
{
handler(this, e);
}
}
///
/// This is the method that raises the VoiceReceived event
///
/// Data for the VoiceReceived event
/// Do not forget to call the base version when overrinding,
/// otherwise the VoiceReceived Event will not fire
///
protected virtual void OnVoiceReceived(LinxAudioEventArgs e)
{
EventHandler handler = VoiceReceived;
if (handler != null)
handler(this, e);
}
private void OnRegistrationStateChanged(RegistrationStateChangedEventArgs e)
{
EventHandler handler = RegistrationStateChanged;
if (handler != null)
handler(this, e);
}
private void OnSipSmsReceived(SmsReceivedEventsArgs e)
{
EventHandler handler = SipSmsReceived;
if (handler != null)
handler(this, e);
}
private void OnGpsPeriodicallyReportReceived(GpsDataEventArgs e)
{
EventHandler handler = GpsPeriodicallyReportReceived;
if (handler != null)
handler(this, e);
}
private void OnLinxEmergencyAlarmReceived(Linx.LinxEmergencyAlarmReceivedEventArgs e)
{
EventHandler handler = EmergencyAlarmReceived;
if (handler != null)
handler(this, e);
}
private void OnGpsReportReceived(LinxGpsDataEventArgs e)
{
EventHandler handler = GpsReportReceived;
if (handler != null)
handler(this, e);
}
private void OnLinxArsReceived(Linx.ArsReceivedEventArgs e)
{
EventHandler handler = LinxArsReceivedOnSip;
if (handler != null)
handler(this, e);
}
#endregion
}
}