基本信息
源码名称:C#基于LumiSoft 实现SIP客户端
源码大小:9.49M
文件格式:.zip
开发语言:C#
更新时间:2019-08-30
友情提示:(无需注册或充值,赞助后即可获取资源下载链接)
嘿,亲!知识可是无价之宝呢,但咱这精心整理的资料也耗费了不少心血呀。小小地破费一下,绝对物超所值哦!如有下载和支付问题,请联系我们QQ(微信同号):813200300
本次赞助数额为: 2 元×
微信扫码支付:2 元
×
请留下您的邮箱,我们将在2小时内将文件发到您的邮箱
源码介绍
using System; using System.IO; using System.Collections.Generic; using System.Linq; using System.Text; using System.Drawing; using System.Windows.Forms; using System.Net; using System.Threading; using LumiSoft.Net; using LumiSoft.Net.SDP; using LumiSoft.Net.SIP; using LumiSoft.Net.SIP.Debug; using LumiSoft.Net.SIP.Stack; using LumiSoft.Net.SIP.Message; using LumiSoft.Net.Media; using LumiSoft.Net.Media.Codec; using LumiSoft.Net.Media.Codec.Audio; using LumiSoft.Net.RTP; using LumiSoft.Net.RTP.Debug; using LumiSoft.Net.STUN.Client; using LumiSoft.Net.UPnP.NAT; using LumiSoft.SIP.UA; using LumiSoft.SIP.UA.Resources; using LumiSoft.Net.AUTH; namespace LumiSoft.SIP.UA.UI { /// <summary> /// Application main window. /// </summary> public class wfrm_Main : Form { private MenuStrip m_pMenu = null; private ToolStrip m_pToolbar = null; private ComboBox m_pAccounts = null; private Button m_pCall = null; private Button m_pIM = null; private ComboBox m_pCallUriType = null; private ComboBox m_pCallUri = null; private TabControl m_pCallTab = null; private ListView m_pCallTab_Contacts_Contacts = null; private wctrl_CallList m_pCallTab_Calls_Calls = null; private ListView m_pCallTab_Status_Registrations = null; private bool m_IsDisposing = false; private bool m_IsDebug = true; private Settings m_pSettings = null; private SIP_Stack m_pStack = null; private List<wfrm_IM> m_pActiveIM = null; private wfrm_SIP_Debug m_pDebugFrom = null; private string m_NatHandlingType = ""; private string m_StunServer = "172.17.17.131"; private UPnP_NAT_Client m_pUPnP = null; private Dictionary<int,AudioCodec> m_pAudioCodecs = null; private AudioOutDevice m_pAudioOutDevice = null; private AudioInDevice m_pAudioInDevice = null; private int m_RtpBasePort = 12000; /// <summary> /// Default constructor. /// </summary> /// <param name="isDebug">Specifies if debug windows showed.</param> public wfrm_Main(bool isDebug) { InitUI(); m_IsDebug = isDebug; m_pSettings = new Settings(); m_pStack = new SIP_Stack(); m_pStack.UserAgent = "LumiSoft SIP UA 1.0"; m_pStack.Error = new EventHandler<ExceptionEventArgs>(m_pStack_Error); m_pActiveIM = new List<wfrm_IM>(); // Show SIP debug UI if in debug mode. if(m_IsDebug){ m_pDebugFrom = new wfrm_SIP_Debug(m_pStack); m_pDebugFrom.Show(); } // Remove me: m_pCallUri.Items.Add("1003@172.17.17.131"); m_pCallUri.Items.Add("tel@lumisoft.ee"); m_pCallUri.Items.Add("music@iptel.org"); m_pCallUri.Items.Add("4151595@services.sip.is"); } #region method InitUI /// <summary> /// Creates and initializes UI. /// </summary> private void InitUI() { this.ClientSize = new Size(300,400); this.StartPosition = FormStartPosition.CenterScreen; this.Icon = ResManager.GetIcon("app.ico",new Size(16,16)); this.Text = "SIP"; this.FormClosed = new FormClosedEventHandler(wfrm_Main_FormClosed); this.VisibleChanged = new EventHandler(wfrm_Main_VisibleChanged); m_pMenu = new MenuStrip(); m_pMenu.Size = new Size(140,25); m_pMenu.Location = new Point(0,0); m_pMenu.Dock = DockStyle.None; ToolStripMenuItem menu_file = new ToolStripMenuItem("File"); ToolStripMenuItem menu_file_exit = new ToolStripMenuItem("Exit"); menu_file_exit.Tag = "exit"; menu_file_exit.Click = new EventHandler(m_pMenu_ItemClicked); menu_file.DropDownItems.Add(menu_file_exit); m_pMenu.Items.Add(menu_file); ToolStripMenuItem menu_settings = new ToolStripMenuItem("Settings"); ToolStripMenuItem menu_settings_proxy = new ToolStripMenuItem("Proxy Settings"); menu_settings_proxy.Tag = "proxy"; menu_settings_proxy.Click = new EventHandler(m_pMenu_ItemClicked); menu_settings.DropDownItems.Add(menu_settings_proxy); ToolStripMenuItem menu_settings_credentials = new ToolStripMenuItem("Credentials"); menu_settings_credentials.Tag = "credentials"; menu_settings_credentials.Click = new EventHandler(m_pMenu_ItemClicked); menu_settings.DropDownItems.Add(menu_settings_credentials); ToolStripMenuItem menu_settings_accounts = new ToolStripMenuItem("Accounts"); menu_settings_accounts.Tag = "accounts"; menu_settings_accounts.Click = new EventHandler(m_pMenu_ItemClicked); menu_settings.DropDownItems.Add(menu_settings_accounts); ToolStripMenuItem menu_settings_audio = new ToolStripMenuItem("Audio"); menu_settings_audio.Tag = "audio"; menu_settings_audio.Click = new EventHandler(m_pMenu_ItemClicked); menu_settings.DropDownItems.Add(menu_settings_audio); m_pMenu.Items.Add(menu_settings); m_pToolbar = new ToolStrip(); m_pToolbar.Size = new Size(70,20); m_pToolbar.Location = new Point(195,0); m_pToolbar.Dock = DockStyle.None; m_pToolbar.Renderer = new ToolBarRendererEx(); //--- Audio button ToolStripDropDownButton button_Audio = new ToolStripDropDownButton(); button_Audio.Name = "audio"; button_Audio.Image = ResManager.GetIcon("speaker.ico").ToBitmap(); foreach(AudioOutDevice device in AudioOut.Devices){ ToolStripMenuItem item = new ToolStripMenuItem(device.Name); item.Checked = (button_Audio.DropDownItems.Count == 0); item.Tag = device; button_Audio.DropDownItems.Add(item); } button_Audio.DropDown.ItemClicked = new ToolStripItemClickedEventHandler(m_pToolBar_Audio_ItemClicked); m_pToolbar.Items.Add(button_Audio); //--- Microphone button ToolStripDropDownButton button_Mic = new ToolStripDropDownButton(); button_Mic.Name = "mic"; button_Mic.Image = ResManager.GetIcon("mic.ico").ToBitmap(); foreach(AudioInDevice device in AudioIn.Devices){ ToolStripMenuItem item = new ToolStripMenuItem(device.Name); item.Checked = (button_Mic.DropDownItems.Count == 0); item.Tag = device; button_Mic.DropDownItems.Add(item); } button_Mic.DropDown.ItemClicked = new ToolStripItemClickedEventHandler(m_pToolBar_Mic_ItemClicked); m_pToolbar.Items.Add(button_Mic); // Separator m_pToolbar.Items.Add(new ToolStripSeparator()); // NAT ToolStripDropDownButton button_NAT = new ToolStripDropDownButton(); button_NAT.Name = "nat"; button_NAT.Image = ResManager.GetIcon("router.ico").ToBitmap(); button_NAT.DropDown.ItemClicked = new ToolStripItemClickedEventHandler(m_pToolbar_NAT_DropDown_ItemClicked); m_pToolbar.Items.Add(button_NAT); m_pAccounts = new ComboBox(); m_pAccounts.Size = new Size(190,20); m_pAccounts.Location = new Point(5,40); m_pAccounts.DropDownStyle = ComboBoxStyle.DropDownList; m_pCall = new Button(); m_pCall.Size = new Size(45,45); m_pCall.Location = new Point(202,40); m_pCall.Image = ResManager.GetIcon("call.ico",new Size(24,24)).ToBitmap(); m_pCall.Click = new EventHandler(m_pCall_Click); m_pIM = new Button(); m_pIM.Size = new Size(45,45); m_pIM.Location = new Point(250,40); m_pIM.Image = ResManager.GetIcon("im.ico",new Size(24,24)).ToBitmap(); m_pIM.Click = new EventHandler(m_pIM_Click); m_pCallUriType = new ComboBox(); m_pCallUriType.Size = new Size(45,20); m_pCallUriType.Location = new Point(5,65); m_pCallUriType.Items.Add("sip:"); m_pCallUriType.SelectedIndex = 0; m_pCallUri = new ComboBox(); m_pCallUri.Size = new Size(145,20); m_pCallUri.Location = new Point(50,65); m_pCallTab = new TabControl(); m_pCallTab.Size = new Size(300,300); m_pCallTab.Location = new Point(0,100); m_pCallTab.Anchor = AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top; m_pCallTab.TabPages.Add("contacts","Contacts"); m_pCallTab.TabPages.Add("calls","Active Calls"); m_pCallTab.TabPages.Add("history","History"); m_pCallTab.TabPages.Add("status","Status"); #region Contacts tab m_pCallTab.TabPages["contacts"].BackColor = Color.White; m_pCallTab_Contacts_Contacts = new ListView(); m_pCallTab_Contacts_Contacts.Size = new Size(260,200); m_pCallTab_Contacts_Contacts.Location = new Point(0,0); m_pCallTab_Contacts_Contacts.Anchor = AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top; m_pCallTab_Contacts_Contacts.BorderStyle = BorderStyle.None; m_pCallTab_Contacts_Contacts.View = View.Details; m_pCallTab_Contacts_Contacts.FullRowSelect = true; m_pCallTab_Contacts_Contacts.HeaderStyle = ColumnHeaderStyle.None; m_pCallTab_Contacts_Contacts.MouseUp = new MouseEventHandler(m_pCallTab_Contacts_Contacts_MouseUp); m_pCallTab_Contacts_Contacts.Columns.Add("Name",240); m_pCallTab.TabPages["contacts"].Controls.Add(m_pCallTab_Contacts_Contacts); #endregion #region Acive calls tab m_pCallTab.TabPages["calls"].BackColor = Color.White; m_pCallTab_Calls_Calls = new wctrl_CallList(); m_pCallTab_Calls_Calls.Dock = DockStyle.Fill; m_pCallTab_Calls_Calls.BorderStyle = BorderStyle.None; m_pCallTab.TabPages["calls"].Controls.Add(m_pCallTab_Calls_Calls); #endregion #region Status tab m_pCallTab.TabPages["status"].BackColor = Color.White; ImageList callTab_Satus_RegistrationsImages = new ImageList(); callTab_Satus_RegistrationsImages.Images.Add(ResManager.GetIcon("register_ok.ico",new Size(16,16))); callTab_Satus_RegistrationsImages.Images.Add(ResManager.GetIcon("register_error.ico",new Size(16,16))); callTab_Satus_RegistrationsImages.Images.Add(ResManager.GetIcon("register_registering.ico",new Size(16,16))); m_pCallTab_Status_Registrations = new ListView(); m_pCallTab_Status_Registrations.Size = new Size(260,200); m_pCallTab_Status_Registrations.Location = new Point(0,0); m_pCallTab_Status_Registrations.Anchor = AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top; m_pCallTab_Status_Registrations.BorderStyle = BorderStyle.None; m_pCallTab_Status_Registrations.View = View.Details; m_pCallTab_Status_Registrations.FullRowSelect = true; m_pCallTab_Status_Registrations.ShowItemToolTips = true; m_pCallTab_Status_Registrations.SmallImageList = callTab_Satus_RegistrationsImages; m_pCallTab_Status_Registrations.Columns.Add("Registraion",180); m_pCallTab_Status_Registrations.Columns.Add("Status",80); m_pCallTab.TabPages["status"].Controls.Add(m_pCallTab_Status_Registrations); #endregion this.Controls.Add(m_pMenu); this.Controls.Add(m_pToolbar); this.Controls.Add(m_pAccounts); this.Controls.Add(m_pIM); this.Controls.Add(m_pCallUriType); this.Controls.Add(m_pCallUri); this.Controls.Add(m_pCall); this.Controls.Add(m_pCallTab); m_pCallTab.SelectedTab = m_pCallTab.TabPages["status"]; } #endregion #region Events handling #region method m_pMenu_ItemClicked /// <summary> /// This method is called when main menu item clicked. /// </summary> /// <param name="sender">Sender.</param> /// <param name="e">Event data.</param> private void m_pMenu_ItemClicked(object sender,EventArgs e) { ToolStripMenuItem item = (ToolStripMenuItem)sender; if(item.Tag == null){ return; } else if(item.Tag.ToString() == "accounts"){ wfrm_Settings frm = new wfrm_Settings(); frm.ShowDialog(this); /* wfrm_Settings_Accounts frm = new wfrm_Settings_Accounts(); if(frm.ShowDialog(this) == DialogResult.OK){ Restart(); }*/ } else if(item.Tag.ToString() == "exit"){ this.Close(); } } #endregion #region method m_pToolBar_Audio_ItemClicked /// <summary> /// Is called when new audio-out device is selected. /// </summary> /// <param name="sender">Sender.</param> /// <param name="e">Event data.</param> private void m_pToolBar_Audio_ItemClicked(object sender,ToolStripItemClickedEventArgs e) { try{ foreach(ToolStripMenuItem item in ((ToolStripDropDownMenu)sender).Items){ if(item.Equals(e.ClickedItem)){ item.Checked = true; } else{ item.Checked = false; } } m_pAudioOutDevice = (AudioOutDevice)e.ClickedItem.Tag; foreach(wctrl_Call call in m_pCallTab_Calls_Calls.Calls){ call.AudioOutDevice = m_pAudioOutDevice; } } catch(Exception x){ MessageBox.Show("Error: " x.Message,"Error:",MessageBoxButtons.OK,MessageBoxIcon.Error); } } #endregion #region method m_pToolBar_Mic_ItemClicked /// <summary> /// Is called when new audio-in device is selected. /// </summary> /// <param name="sender">Sender.</param> /// <param name="e">Event data.</param> private void m_pToolBar_Mic_ItemClicked(object sender,ToolStripItemClickedEventArgs e) { try{ foreach(ToolStripMenuItem item in ((ToolStripDropDownMenu)sender).Items){ if(item.Equals(e.ClickedItem)){ item.Checked = true; } else{ item.Checked = false; } } m_pAudioInDevice = (AudioInDevice)e.ClickedItem.Tag; foreach(wctrl_Call call in m_pCallTab_Calls_Calls.Calls){ call.AudioInDevice = m_pAudioInDevice; } } catch(Exception x){ MessageBox.Show("Error: " x.Message,"Error:",MessageBoxButtons.OK,MessageBoxIcon.Error); } } #endregion #region method m_pToolbar_NAT_DropDown_ItemClicked /// <summary> /// Is called when new NAT handling method is selected. /// </summary> /// <param name="sender">Sender.</param> /// <param name="e">Event data.</param> private void m_pToolbar_NAT_DropDown_ItemClicked(object sender,ToolStripItemClickedEventArgs e) { if(!e.ClickedItem.Enabled){ return; } foreach(ToolStripMenuItem item in ((ToolStripDropDownMenu)sender).Items){ if(item.Equals(e.ClickedItem)){ item.Checked = true; m_NatHandlingType = item.Name; } else{ item.Checked = false; } } } #endregion #region method m_pCall_Click /// <summary> /// This method is called when call button is pressed. /// </summary> /// <param name="sender">Sender.</param> /// <param name="e">Event data.</param> private void m_pCall_Click(object sender,EventArgs e) { if(m_pCallUri.Text == string.Empty){ MessageBox.Show(this,"Please fill call URI !","Error:",MessageBoxButtons.OK,MessageBoxIcon.Error); return; } string uri = m_pCallUri.Text; if(uri.IndexOf(':') == -1){ uri = "sip:" uri; } Call(((Account)((WComboBoxItem)m_pAccounts.SelectedItem).Tag),new SIP_t_NameAddress(uri)); m_pCallUri.Text = ""; m_pCallTab.SelectedTab = m_pCallTab.TabPages["calls"]; } #endregion #region method m_pIM_Click /// <summary> /// This method is called when IM button is clicked. /// </summary> /// <param name="sender">Sender.</param> /// <param name="e">Event data.</param> private void m_pIM_Click(object sender,EventArgs e) { if(m_pCallUri.Text == string.Empty){ MessageBox.Show(this,"Please fill call URI !","Error:",MessageBoxButtons.OK,MessageBoxIcon.Error); return; } string uri = m_pCallUri.Text; if(uri.IndexOf(':') == -1){ uri = "sip:" uri; } OpenIM(((Account)((WComboBoxItem)m_pAccounts.SelectedItem).Tag),new SIP_t_NameAddress(uri)); m_pCallUri.Text = ""; } #endregion #region method wfrm_Main_FormClosed /// <summary> /// This method is called when main form is closed. /// </summary> /// <param name="sender">Sender.</param> /// <param name="e">Event data.</param> private void wfrm_Main_FormClosed(object sender,FormClosedEventArgs e) { m_IsDisposing = true; if(m_pDebugFrom != null){ m_pDebugFrom.Close(); } m_pStack.Stop(); m_pStack.Dispose(); } #endregion #region method wfrm_Main_VisibleChanged /// <summary> /// This method is called when main windows visibility changes. /// </summary> /// <param name="sender">Sender.</param> /// <param name="e">Event data.</param> private void wfrm_Main_VisibleChanged(object sender, EventArgs e) { if(this.Visible){ LoadSettings(); } } #endregion #region method IM_FormClosed /// <summary> /// This method is called when IM window has closed. /// </summary> /// <param name="sender">Sender.</param> /// <param name="e">Event data.</param> private void IM_FormClosed(object sender,FormClosedEventArgs e) { m_pActiveIM.Remove((wfrm_IM)sender); } #endregion #region method m_pCallTab_Contacts_Contacts_MouseUp /// <summary> /// This method is called when contacts list mouse button is released. /// </summary> /// <param name="sender">Sender.</param> /// <param name="e">Event data.</param> private void m_pCallTab_Contacts_Contacts_MouseUp(object sender,MouseEventArgs e) { if(e.Button != MouseButtons.Right){ return; } ContextMenuStrip menu = new ContextMenuStrip(); ToolStripMenuItem menu_Add = new ToolStripMenuItem("New Contact"); menu_Add.Click = new EventHandler(delegate(object s1,EventArgs e1){ // TODO: }); menu.Items.Add(menu_Add); if(m_pCallTab_Contacts_Contacts.SelectedItems.Count > 0){ menu.Items.Add(new ToolStripSeparator()); ToolStripMenuItem menu_Call = new ToolStripMenuItem("Call"); menu_Call.Click = new EventHandler(delegate(object s1,EventArgs e1){ // TODO: // FIX ME: string uri = m_pCallTab_Contacts_Contacts.SelectedItems[0].Text; if(uri.IndexOf(':') == -1){ uri = "sip:" uri; } m_pCallTab.SelectedTab = m_pCallTab.TabPages["calls"]; Call(((Account)((WComboBoxItem)m_pAccounts.SelectedItem).Tag),new SIP_t_NameAddress(uri)); }); menu.Items.Add(menu_Call); ToolStripMenuItem menu_IM = new ToolStripMenuItem("IM"); menu_IM.Click = new EventHandler(delegate(object s1,EventArgs e1){ // TODO: // FIX ME: string uri = m_pCallTab_Contacts_Contacts.SelectedItems[0].Text; if(uri.IndexOf(':') == -1){ uri = "sip:" uri; } OpenIM(((Account)((WComboBoxItem)m_pAccounts.SelectedItem).Tag),new SIP_t_NameAddress(uri)); }); menu.Items.Add(menu_IM); } menu.Show(m_pCallTab_Contacts_Contacts,e.Location); } #endregion #region method m_pCallTab_Calls_Calls_MouseUp /// <summary> /// This method is called when calls list mouse button is released. /// </summary> /// <param name="sender">Sender.</param> /// <param name="e">Event data.</param> private void m_pCallTab_Calls_Calls_MouseUp(object sender,MouseEventArgs e) { if(e.Button != MouseButtons.Right){ return; } /* if(m_pCallTab_Calls_Calls.SelectedItems.Count > 0){ Call call = (Call)m_pCallTab_Calls_Calls.SelectedItems[0].Tag; ContextMenuStrip menu = new ContextMenuStrip(); if(call.State == CallState.WaitingToAccept){ // Accept ToolStripMenuItem accept = new ToolStripMenuItem("Accept"); accept.Click = new EventHandler(delegate(object s1,EventArgs e1){ AcceptCall(call); }); menu.Items.Add(accept); } if(call.State == CallState.Active){ bool onHold = false; if(call.Tags.ContainsKey("OnHold")){ onHold = ((bool)call.Tags["OnHold"]); } if(onHold){ // Take off on hold ToolStripMenuItem putOnHold = new ToolStripMenuItem("Take Off On Hold"); putOnHold.Image = ResManager.GetIcon("call_hangup.ico",new Size(16,16)).ToBitmap(); putOnHold.Click = new EventHandler(delegate(object s1,EventArgs e1){ ToggleCallOnHold(call); }); menu.Items.Add(putOnHold); } else{ // Put on hold ToolStripMenuItem putOnHold = new ToolStripMenuItem("Put On Hold"); putOnHold.Image = ResManager.GetIcon("call_hangup.ico",new Size(16,16)).ToBitmap(); putOnHold.Click = new EventHandler(delegate(object s1,EventArgs e1){ ToggleCallOnHold(call); }); menu.Items.Add(putOnHold); } } // Separator if(menu.Items.Count > 0){ menu.Items.Add(new ToolStripSeparator()); } // Hang Up ToolStripMenuItem hangUp = new ToolStripMenuItem("Hang Up"); hangUp.Image = ResManager.GetIcon("call_hangup.ico",new Size(16,16)).ToBitmap(); hangUp.Click = new EventHandler(delegate(object s1,EventArgs e1){ call.Terminate("Hang Up"); }); menu.Items.Add(hangUp); //-------- menu.Show(m_pCallTab_Calls_Calls,e.Location); }*/ } #endregion #region method Registration_StateChanged /// <summary> /// This method is called when registration state has changed. /// </summary> /// <param name="sender">Sender.</param> /// <param name="e">Event data.</param> private void Registration_StateChanged(object sender,EventArgs e) { if(m_IsDisposing){ return; } try{ this.BeginInvoke( new MethodInvoker(delegate(){ SIP_UA_Registration registration = (SIP_UA_Registration)sender; // Remove registration. if(registration.State == SIP_UA_RegistrationState.Disposed){ foreach(ListViewItem it in m_pCallTab_Status_Registrations.Items){ if(it.Text == registration.AOR){ it.Remove(); break; } } } // Update registration state. else{ ListViewItem regItem = null; foreach(ListViewItem it in m_pCallTab_Status_Registrations.Items){ if(it.Text == registration.AOR){ regItem = it; break; } } // New registration, create it to UI. if(regItem == null){ regItem = new ListViewItem(registration.AOR); regItem.SubItems.Add(""); m_pCallTab_Status_Registrations.Items.Add(regItem); } if(registration.State == SIP_UA_RegistrationState.Registered){ regItem.ImageIndex = 0; StringBuilder tooltipText = new StringBuilder(); tooltipText.AppendLine("AOR: " registration.AOR); tooltipText.AppendLine("TTL: " registration.Expires); foreach(AbsoluteUri contactUri in registration.Contacts){ tooltipText.AppendLine("Contact: " contactUri.ToString()); } regItem.ToolTipText = tooltipText.ToString(); } else if(registration.State == SIP_UA_RegistrationState.Error){ regItem.ImageIndex = 1; //regItem.ToolTipText } else if(registration.State == SIP_UA_RegistrationState.Registering){ regItem.ImageIndex = 2; } else{ regItem.ImageIndex = -1; } regItem.SubItems[1].Text = registration.State.ToString(); } }), null ); } catch(Exception x){ string dummy = x.Message; // Skip, UA closing. } } #endregion #region method m_pStack_RequestReceived /// <summary> /// This method is called when SIP stack get new incoming request. /// </summary> /// <param name="sender">Sender.</param> /// <param name="e">Event data.</param> private void m_pStack_RequestReceived(object sender,SIP_RequestReceivedEventArgs e) { try{ #region Validate request-URI /* Block rules. *) Block non SIP URI. *) Block non local AOR. */ if(!(e.Request.RequestLine.Uri is SIP_Uri)){ e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x416_Unsupported_URI_Scheme,e.Request)); return; } SIP_Uri requestUri = (SIP_Uri)e.Request.RequestLine.Uri; // If request URI is our registration contact, get AOR for it. string aor = requestUri.Address; foreach(SIP_UA_Registration registration in m_pStack.Registrations){ foreach(AbsoluteUri contactUri in registration.Contacts){ if(requestUri.Equals(contactUri)){ aor = registration.AOR; break; } } } Account localAccount = null; foreach(WComboBoxItem it in m_pAccounts.Items){ Account account = (Account)it.Tag; if(string.Equals(account.AOR,aor,StringComparison.InvariantCultureIgnoreCase)){ localAccount = account; break; } } if(localAccount == null){ e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x404_Not_Found,e.Request)); return; } #endregion #region MESSAGE if(e.Request.RequestLine.Method == SIP_Methods.MESSAGE){ wfrm_IM frmIM = null; // If active IM window, use it. foreach(wfrm_IM frm in m_pActiveIM){ if(frm.TargetURI.Uri.ToString() == e.Request.From.Address.Uri.ToString()){ frmIM = frm; break; } } if(frmIM == null){ // See if in contacts list // Open IM window // Ask user to allow/block contact this.Invoke( new MethodInvoker(delegate(){ frmIM = OpenIM(localAccount,e.Request.From.Address); }), null ); } frmIM.ProcessIM(e.Request); // Send 200 response. e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x200_Ok,e.Request)); } #endregion #region INVITE else if(e.Request.RequestLine.Method == SIP_Methods.INVITE){ #region Incoming call if(e.Dialog == null){ #region Validate incoming call // TODO: Limit incoming calls count // We don't accept more than 1 call at time. //if(m_pIncomingCallUI != null || m_pCall != null){ // e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x600_Busy_Everywhere,e.Request)); // return; //} // We don't accept SDP offerless calls. if(e.Request.ContentType == null || e.Request.ContentType.ToLower().IndexOf("application/sdp") == -1){ e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x606_Not_Acceptable ": We don't accpet SDP offerless calls.",e.Request)); return; } SDP_Message sdpOffer = SDP_Message.Parse(Encoding.UTF8.GetString(e.Request.Data)); // Check if we can accept any media stream. bool canAccept = false; foreach(SDP_MediaDescription media in sdpOffer.MediaDescriptions){ if(CanSupportMedia(media)){ canAccept = true; break; } } if(!canAccept){ e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x606_Not_Acceptable,e.Request)); return; } #endregion // We need BeginInvoke here to access UI, we are running on thread pool thread. this.BeginInvoke(new MethodInvoker(delegate(){ m_pCallTab.SelectedTab = m_pCallTab.TabPages["calls"]; // Create call, all continuing processing done in call class. wctrl_Call call = new wctrl_Call(this,m_pAudioOutDevice,m_pAudioInDevice); m_pCallTab_Calls_Calls.AddCall(call); call.InitIncoming(localAccount,e.ServerTransaction); })); } #endregion else{ // Unknown error, call didn't process re-INVITE. e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x500_Server_Internal_Error,e.Request)); } } #endregion #region CANCEL else if(e.Request.RequestLine.Method == SIP_Methods.CANCEL){ /* RFC 3261 9.2. If the UAS did not find a matching transaction for the CANCEL according to the procedure above, it SHOULD respond to the CANCEL with a 481 (Call Leg/Transaction Does Not Exist). Regardless of the method of the original request, as long as the CANCEL matched an existing transaction, the UAS answers the CANCEL request itself with a 200 (OK) response. */ SIP_ServerTransaction trToCancel = m_pStack.TransactionLayer.MatchCancelToTransaction(e.Request); if(trToCancel != null){ trToCancel.Cancel(); e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x200_Ok,e.Request)); } else{ e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x481_Call_Transaction_Does_Not_Exist,e.Request)); } } #endregion #region BYE else if(e.Request.RequestLine.Method == SIP_Methods.BYE){ /* RFC 3261 15.1.2. If the BYE does not match an existing dialog, the UAS core SHOULD generate a 481 (Call/Transaction Does Not Exist) response and pass that to the server transaction. */ // Currently we match BYE to dialog and it processes it, // so BYE what reaches here doesnt match to any dialog. e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x481_Call_Transaction_Does_Not_Exist,e.Request)); } #endregion #region ACK else if(e.Request.RequestLine.Method == SIP_Methods.ACK){ // Abandoned ACK, just skip it. } #endregion #region Other else { // ACK is response less method. if(e.Request.RequestLine.Method != SIP_Methods.ACK){ e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x501_Not_Implemented,e.Request)); } } #endregion } catch(Exception x){ e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x500_Server_Internal_Error,e.Request)); OnError(x); } } #endregion #region method m_pStack_Error /// <summary> /// Is called when SIP stack has unhandled error. /// </summary> /// <param name="sender">Sender.</param> /// <param name="e">Event data.</param> private void m_pStack_Error(object sender,ExceptionEventArgs e) { // TODO: Console.WriteLine("Error: " e.Exception.ToString()); } #endregion #endregion #region method Call /// <summary> /// Calls to the specified person. /// </summary> /// <param name="account">Local account to use.</param> /// <param name="to">Call target address.</param> /// <exception cref="ArgumentNullException">Is raised when <b>account</b> or <b>to</b> is null reference.</exception> public void Call(Account account, SIP_t_NameAddress to) { if (account == null) { throw new ArgumentNullException("account"); } if (to == null) { throw new ArgumentNullException("to"); } PutAllCallsOnHold(); // Create call and start calling. wctrl_Call call = new wctrl_Call(this, m_pAudioOutDevice, m_pAudioInDevice); m_pCallTab_Calls_Calls.AddCall(call); // Move processing to thread pool, otherwise UI hangs. ThreadPool.QueueUserWorkItem(delegate(object state) { call.InitCalling(account, to); }); } #endregion #region method OpenIM /// <summary> /// Opens new IM window to the specified recipient. /// </summary> /// <param name="account">Local account.</param> /// <param name="to">Remote party.</param> /// <returns>Returns created IM window.</returns> /// <exception cref="ArgumentNullException">Is raised when <b>account</b> or <b>to</b> is null reference.</exception> public wfrm_IM OpenIM(Account account,SIP_t_NameAddress to) { if(account == null){ throw new ArgumentNullException("account"); } if(to == null){ throw new ArgumentNullException("to"); } wfrm_IM frmIM = new wfrm_IM(this,account,to); m_pActiveIM.Add(frmIM); frmIM.FormClosed = new FormClosedEventHandler(IM_FormClosed); frmIM.Visible = true; return frmIM; } #endregion #region method PutAllCallsOnHold /// <summary> /// Puts all calls on hold. /// </summary> internal void PutAllCallsOnHold() { // Put all active calls on hold. foreach(wctrl_Call c in this.Calls){ if(c.State == SIP_CallState.Active && !c.IsLocalOnHold){ c.PutCallOnHold(); } } // Wait calls to switch to on-hold. DateTime start = DateTime.Now; while(start.AddSeconds(5) > DateTime.Now){ bool allonHold = true; foreach(wctrl_Call c in this.Calls){ if(c.State == SIP_CallState.Active && !c.IsLocalOnHold){ allonHold = false; } } if(allonHold){ break; } else{ Thread.Sleep(200); } } } #endregion #region method LoadSettings /// <summary> /// Loads settings from xml file. /// </summary> private void LoadSettings() { if(File.Exists(Application.StartupPath "\\Settings\\Settings.xml")){ this.Settings.Load(Application.StartupPath "\\Settings\\Settings.xml"); } #region Init audio devices if(AudioOut.Devices.Length == 0){ foreach(Control control in this.Controls){ control.Enabled = false; } MessageBox.Show("Calling not possible, there are no speakers in computer.","Error:",MessageBoxButtons.OK,MessageBoxIcon.Error); return; } if(AudioIn.Devices.Length == 0){ foreach(Control control in this.Controls){ control.Enabled = false; } MessageBox.Show("Calling not possible, there is no microphone in computer.","Error:",MessageBoxButtons.OK,MessageBoxIcon.Error); return; } m_pAudioOutDevice = AudioOut.Devices[0]; m_pAudioInDevice = AudioIn.Devices[0]; m_pAudioCodecs = new Dictionary<int,AudioCodec>(); m_pAudioCodecs.Add(0,new PCMU()); m_pAudioCodecs.Add(8,new PCMA()); #endregion #region Get NAT handling methods m_pUPnP = new UPnP_NAT_Client(); STUN_Result stunResult = new STUN_Result(STUN_NetType.UdpBlocked,null); try{ stunResult = STUN_Client.Query(m_StunServer, 5060, new IPEndPoint(IPAddress.Any, 0)); } catch{ } if(stunResult.NetType == STUN_NetType.Symmetric || stunResult.NetType == STUN_NetType.UdpBlocked){ ToolStripMenuItem item_stun = new ToolStripMenuItem("STUN (" stunResult.NetType ")"); item_stun.Name = "stun"; item_stun.Enabled = false; ((ToolStripDropDownButton)m_pToolbar.Items["nat"]).DropDownItems.Add(item_stun); } else{ ToolStripMenuItem item_stun = new ToolStripMenuItem("STUN (" stunResult.NetType ")"); item_stun.Name = "stun"; ((ToolStripDropDownButton)m_pToolbar.Items["nat"]).DropDownItems.Add(item_stun); } if(m_pUPnP.IsSupported){ ToolStripMenuItem item_upnp = new ToolStripMenuItem("UPnP"); item_upnp.Name = "upnp"; ((ToolStripDropDownButton)m_pToolbar.Items["nat"]).DropDownItems.Add(item_upnp); } else{ ToolStripMenuItem item_upnp = new ToolStripMenuItem("UPnP Not Supported"); item_upnp.Name = "upnp"; item_upnp.Enabled = false; ((ToolStripDropDownButton)m_pToolbar.Items["nat"]).DropDownItems.Add(item_upnp); } //if(!((ToolStripDropDownButton)m_pToolbar.Items["nat"]).DropDownItems["stun"].Enabled && !((ToolStripDropDownButton)m_pToolbar.Items["nat"]).DropDownItems["upnp"].Enabled){ // MessageBox.Show("Calling may not possible, your firewall or router blocks STUN and doesn't support UPnP.\r\n\r\nSTUN Net Type: " stunResult.NetType "\r\n\r\nUPnP Supported: " m_pUPnP.IsSupported,"Error:",MessageBoxButtons.OK,MessageBoxIcon.Error); //} ToolStripMenuItem item_no_nat = new ToolStripMenuItem("No NAT handling"); item_no_nat.Name = "no_nat"; ((ToolStripDropDownButton)m_pToolbar.Items["nat"]).DropDownItems.Add(item_no_nat); // Select first enabled item. foreach(ToolStripItem it in ((ToolStripDropDownButton)m_pToolbar.Items["nat"]).DropDownItems){ if(it.Enabled){ ((ToolStripMenuItem)it).Checked = true; m_NatHandlingType = it.Name; break; } } #endregion m_pStack.BindInfo = Settings.Bindings; m_pStack.RequestReceived = new EventHandler<SIP_RequestReceivedEventArgs>(m_pStack_RequestReceived); // TODO: //m_pStack.Error m_pStack.Credentials.AddRange(this.Settings.Credentials.ToArray()); m_pStack.Start(); foreach(Account account in this.Settings.Accounts){ if(account.Register){ SIP_Uri registrarServer = SIP_Uri.Parse(account.RegistrarServer); SIP_UA_Registration registration = m_pStack.CreateRegistration( registrarServer, account.AOR, AbsoluteUri.Parse(registrarServer.Scheme ":" account.AOR.Split('@')[0] "@172.17.17.131"), account.RegisterInterval ); registration.StateChanged = new EventHandler(Registration_StateChanged); registration.BeginRegister(true); // TODO: If TLS supported, add TLS registration too. } m_pAccounts.Items.Add(new WComboBoxItem(account.AOR,account)); } if(m_pAccounts.Items.Count > 0){ m_pAccounts.SelectedIndex = 0; } } #endregion #region method Restart /// <summary> /// Starts/restarts SIP UA. /// </summary> private void Restart() { // TODO: } #endregion #region method OnError /// <summary> /// This method is called when unhandled exception occured. /// </summary> /// <param name="x">Exception.</param> private void OnError(Exception x) { // TODO: FIX ME: Console.WriteLine("Error:" x.ToString()); } #endregion #region method CanSupportMedia /// <summary> /// Checks if we can support the specified media. /// </summary> /// <param name="media">SDP media.</param> /// <returns>Returns true if we can support this media, otherwise false.</returns> /// <exception cref="ArgumentNullException">Is raised when <b>media</b> is null reference.</exception> internal bool CanSupportMedia(SDP_MediaDescription media) { if(media == null){ throw new ArgumentNullException("media"); } if(!string.Equals(media.MediaType,SDP_MediaTypes.audio,StringComparison.InvariantCultureIgnoreCase)){ return false; } if(!string.Equals(media.Protocol,"RTP/AVP",StringComparison.InvariantCultureIgnoreCase)){ return false; } if(GetOurSupportedAudioCodecs(media).Count > 0){ return true; } return false; } #endregion #region method GetOurSupportedAudioCodecs /// <summary> /// Gets audio codecs which we can support from SDP media stream. /// </summary> /// <param name="media">SDP media stream.</param> /// <exception cref="ArgumentNullException">Is raised when <b>media</b> is null reference.</exception> /// <returns>Returns audio codecs which support.</returns> internal Dictionary<int,AudioCodec> GetOurSupportedAudioCodecs(SDP_MediaDescription media) { if(media == null){ throw new ArgumentNullException("media"); } Dictionary<int,AudioCodec> codecs = new Dictionary<int,AudioCodec>(); // Check for IANA registered payload. Custom range is 96-127 and always must have rtpmap attribute. foreach(string format in media.MediaFormats){ int payload = Convert.ToInt32(format); if(payload < 96 && m_pAudioCodecs.ContainsKey(payload)){ if(!codecs.ContainsKey(payload)){ codecs.Add(payload,m_pAudioCodecs[payload]); } } } // Check rtpmap payloads. foreach(SDP_Attribute a in media.Attributes){ if(string.Equals(a.Name,"rtpmap",StringComparison.InvariantCultureIgnoreCase)){ // Example: 0 PCMU/8000 string[] parts = a.Value.Split(' '); int payload = Convert.ToInt32(parts[0]); string codecName = parts[1].Split('/')[0]; foreach(AudioCodec codec in m_pAudioCodecs.Values){ if(string.Equals(codec.Name,codecName,StringComparison.InvariantCultureIgnoreCase)){ if(!codecs.ContainsKey(payload)){ codecs.Add(payload,codec); } } } } } return codecs; } #endregion #region method CreateRtpSession /// <summary> /// Creates new RTP session. /// </summary> /// <param name="rtpMultimediaSession">RTP multimedia session.</param> /// <returns>Returns created RTP session or null if failed to create RTP session.</returns> /// <exception cref="ArgumentNullException">Is raised <b>rtpMultimediaSession</b> is null reference.</exception> internal RTP_Session CreateRtpSession(RTP_MultimediaSession rtpMultimediaSession) { if(rtpMultimediaSession == null){ throw new ArgumentNullException("rtpMultimediaSession"); } //--- Search RTP IP -------------------------------------------------------// IPAddress rtpIP = null; foreach(IPAddress ip in Dns.GetHostAddresses("")){ if(ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork){ rtpIP = ip; break; } } if(rtpIP == null){ throw new Exception("None of the network connection is available."); } //------------------------------------------------------------------------// // Search free ports for RTP session. for(int i=0;i<100;i =2){ try{ return rtpMultimediaSession.CreateSession(new RTP_Address(rtpIP,m_RtpBasePort,m_RtpBasePort 1),new RTP_Clock(1,8000)); } catch{ m_RtpBasePort = 2; } } return null; } #endregion #region method HandleNAT /// <summary> /// Handles NAT and stores RTP data to <b>mediaStream</b>. /// </summary> /// <param name="mediaStream">SDP media stream.</param> /// <param name="rtpSession">RTP session.</param> /// <returns>Returns true if NAT handled ok, otherwise false.</returns> internal bool HandleNAT(SDP_MediaDescription mediaStream,RTP_Session rtpSession) { if(mediaStream == null){ throw new ArgumentNullException("mediaStream"); } if(rtpSession == null){ throw new ArgumentNullException("rtpSession"); } IPEndPoint rtpPublicEP = null; IPEndPoint rtcpPublicEP = null; // We have public IP. if(!Net_Utils.IsPrivateIP(rtpSession.LocalEP.IP)){ rtpPublicEP = rtpSession.LocalEP.RtpEP; rtcpPublicEP = rtpSession.LocalEP.RtcpEP; } // No NAT handling. else if(m_NatHandlingType == "no_nat"){ rtpPublicEP = rtpSession.LocalEP.RtpEP; rtcpPublicEP = rtpSession.LocalEP.RtcpEP; } // Use STUN. else if(m_NatHandlingType == "stun"){ rtpSession.StunPublicEndPoints(m_StunServer,3478,out rtpPublicEP,out rtcpPublicEP); } // Use UPnP. else if(m_NatHandlingType == "upnp"){ // Try to open UPnP ports. if(m_pUPnP.IsSupported){ int rtpPublicPort = rtpSession.LocalEP.RtpEP.Port; int rtcpPublicPort = rtpSession.LocalEP.RtcpEP.Port; try{ UPnP_NAT_Map[] maps = m_pUPnP.GetPortMappings(); while(true){ bool conficts = false; // Check that some other application doesn't use that port. foreach(UPnP_NAT_Map map in maps){ // Existing map entry conflicts. if(Convert.ToInt32(map.ExternalPort) == rtpPublicPort || Convert.ToInt32(map.ExternalPort) == rtcpPublicPort){ rtpPublicPort = 2; rtcpPublicPort = 2; conficts = true; break; } } if(!conficts){ break; } } m_pUPnP.AddPortMapping(true,"LS RTP","UDP",null,rtpPublicPort,rtpSession.LocalEP.RtpEP,0); m_pUPnP.AddPortMapping(true,"LS RTCP","UDP",null,rtcpPublicPort,rtpSession.LocalEP.RtcpEP,0); IPAddress publicIP = m_pUPnP.GetExternalIPAddress(); rtpPublicEP = new IPEndPoint(publicIP,rtpPublicPort); rtcpPublicEP = new IPEndPoint(publicIP,rtcpPublicPort); mediaStream.Tags.Add("upnp_rtp_map",new UPnP_NAT_Map(true,"UDP","",rtpPublicPort.ToString(),rtpSession.LocalEP.IP.ToString(),rtpSession.LocalEP.RtpEP.Port,"LS RTP",0)); mediaStream.Tags.Add("upnp_rtcp_map",new UPnP_NAT_Map(true,"UDP","",rtcpPublicPort.ToString(),rtpSession.LocalEP.IP.ToString(),rtpSession.LocalEP.RtcpEP.Port,"LS RTCP",0)); } catch{ } } } if(rtpPublicEP != null && rtcpPublicEP != null){ mediaStream.Port = rtpPublicEP.Port; if((rtpPublicEP.Port 1) != rtcpPublicEP.Port){ // Remove old rport attribute, if any. for(int i=0;i<mediaStream.Attributes.Count;i ){ if(string.Equals(mediaStream.Attributes[i].Name,"rport",StringComparison.InvariantCultureIgnoreCase)){ mediaStream.Attributes.RemoveAt(i); i--; } } mediaStream.Attributes.Add(new SDP_Attribute("rport",rtcpPublicEP.Port.ToString())); } mediaStream.Connection = new SDP_Connection("IN","IP4",rtpPublicEP.Address.ToString()); return true; } return false; } #endregion #region Properties implementation /// <summary> /// Gets UA settings. /// </summary> public Settings Settings { get{ return m_pSettings; } } /// <summary> /// Gets SIP UA. /// </summary> public SIP_Stack SIP_Stack { get{ return m_pStack; } } /// <summary> /// Gets audio codecs. /// </summary> public Dictionary<int,AudioCodec> AudioCodecs { get{ return m_pAudioCodecs; } } /// <summary> /// Gets call list. /// </summary> public wctrl_Call[] Calls { get{ return m_pCallTab_Calls_Calls.Calls; } } /// <summary> /// Gets if debug mode. /// </summary> internal bool IsDebug { get{ return m_IsDebug; } } /// <summary> /// Gets UPnP NAT client. /// </summary> internal UPnP_NAT_Client UPnP { get{ return m_pUPnP; } } #endregion } }