1/* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18using System;
19using System.Drawing;
20using System.Collections;
21using System.ComponentModel;
22using System.Windows.Forms;
23using System.Net;
24using System.Net.Sockets;
25using System.Data;
26using System.Text;
27using Bonjour;
28
29namespace SimpleChat.NET
30{
31	/// <summary>
32	/// Summary description for Form1.
33	/// </summary>
34	///
35
36	public class SimpleChat : System.Windows.Forms.Form
37	{
38		private System.Windows.Forms.ComboBox   comboBox1;
39		private System.Windows.Forms.TextBox    textBox2;
40		private System.Windows.Forms.Button     button1;
41		private System.Windows.Forms.Label      label1;
42
43        private Bonjour.DNSSDEventManager       m_eventManager = null;
44        private Bonjour.DNSSDService            m_service = null;
45        private Bonjour.DNSSDService            m_registrar = null;
46        private Bonjour.DNSSDService            m_browser = null;
47        private Bonjour.DNSSDService            m_resolver = null;
48		private String					        m_name;
49        private Socket                          m_socket = null;
50        private const int                       BUFFER_SIZE = 1024;
51        public byte[]                           m_buffer = new byte[BUFFER_SIZE];
52        public bool                             m_complete = false;
53        public StringBuilder                    m_sb = new StringBuilder();
54
55        delegate void                           ReadMessageCallback(String data);
56
57        ReadMessageCallback                     m_readMessageCallback;
58		/// <summary>
59		/// Required designer variable.
60		/// </summary>
61		private System.ComponentModel.Container components = null;
62		private System.Windows.Forms.RichTextBox richTextBox1;
63
64		// ServiceRegistered
65		//
66		// Called by DNSServices core as a result of Register()
67		// call
68		//
69        public void
70        ServiceRegistered
71                    (
72                    DNSSDService service,
73                    DNSSDFlags flags,
74                    String name,
75                    String regType,
76                    String domain
77                    )
78        {
79            m_name = name;
80
81			//
82			// Try to start browsing for other instances of this service
83			//
84            try
85            {
86                m_browser = m_service.Browse(0, 0, "_p2pchat._udp", null, m_eventManager);
87            }
88            catch
89            {
90                MessageBox.Show("Browse Failed", "Error");
91                Application.Exit();
92            }
93        }
94
95		//
96		// ServiceFound
97		//
98		// Called by DNSServices core as a result of a Browse call
99		//
100		public void
101        ServiceFound
102				    (
103				    DNSSDService    sref,
104				    DNSSDFlags  	flags,
105				    uint			ifIndex,
106                    String          serviceName,
107                    String          regType,
108                    String          domain
109				    )
110		{
111            if (serviceName != m_name)
112            {
113                PeerData peer = new PeerData();
114
115                peer.InterfaceIndex = ifIndex;
116                peer.Name = serviceName;
117                peer.Type = regType;
118                peer.Domain = domain;
119                peer.Address = null;
120
121                comboBox1.Items.Add(peer);
122
123                if (comboBox1.Items.Count == 1)
124                {
125                    comboBox1.SelectedIndex = 0;
126                }
127            }
128		}
129
130        //
131        // ServiceLost
132        //
133        // Called by DNSServices core as a result of a Browse call
134        //
135        public void
136        ServiceLost
137                    (
138                    DNSSDService sref,
139                    DNSSDFlags flags,
140                    uint ifIndex,
141                    String serviceName,
142                    String regType,
143                    String domain
144                    )
145        {
146            PeerData peer = new PeerData();
147
148            peer.InterfaceIndex = ifIndex;
149            peer.Name = serviceName;
150            peer.Type = regType;
151            peer.Domain = domain;
152            peer.Address = null;
153
154            comboBox1.Items.Remove(peer);
155        }
156
157		//
158		// ServiceResolved
159		//
160		// Called by DNSServices core as a result of DNSService.Resolve()
161		// call
162		//
163        public void
164        ServiceResolved
165                    (
166                    DNSSDService sref,
167                    DNSSDFlags flags,
168                    uint ifIndex,
169                    String fullName,
170                    String hostName,
171                    ushort port,
172                    TXTRecord txtRecord
173                    )
174		{
175            m_resolver.Stop();
176            m_resolver = null;
177
178            PeerData peer = (PeerData)comboBox1.SelectedItem;
179
180            peer.Port = port;
181
182			//
183			// Query for the IP address associated with "hostName"
184			//
185            try
186            {
187                m_resolver = m_service.QueryRecord(0, ifIndex, hostName, DNSSDRRType.kDNSSDType_A, DNSSDRRClass.kDNSSDClass_IN, m_eventManager );
188            }
189            catch
190            {
191                MessageBox.Show("QueryRecord Failed", "Error");
192                Application.Exit();
193            }
194		}
195
196		//
197		// QueryAnswered
198		//
199		// Called by DNSServices core as a result of DNSService.QueryRecord()
200		// call
201		//
202		public void
203		QueryAnswered
204			(
205            DNSSDService    service,
206            DNSSDFlags      flags,
207            uint            ifIndex,
208            String          fullName,
209            DNSSDRRType     rrtype,
210            DNSSDRRClass    rrclass,
211            Object          rdata,
212            uint            ttl
213            )
214        {
215			//
216			// Stop the resolve to reduce the burden on the network
217			//
218            m_resolver.Stop();
219            m_resolver = null;
220
221            PeerData peer = (PeerData) comboBox1.SelectedItem;
222			uint bits = BitConverter.ToUInt32( (Byte[])rdata, 0);
223			System.Net.IPAddress address = new System.Net.IPAddress(bits);
224
225            peer.Address = address;
226		}
227
228        public void
229        OperationFailed
230                    (
231                    DNSSDService service,
232                    DNSSDError error
233                    )
234        {
235            MessageBox.Show("Operation returned an error code " + error, "Error");
236        }
237
238        //
239        // OnReadMessage
240        //
241        // Called when there is data to be read on a socket
242        //
243        // This is called (indirectly) from OnReadSocket()
244        //
245        private void
246        OnReadMessage
247                (
248                String msg
249                )
250        {
251            int rgb = 0;
252
253            for (int i = 0; i < msg.Length && msg[i] != ':'; i++)
254            {
255                rgb = rgb ^ ((int)msg[i] << (i % 3 + 2) * 8);
256
257            }
258
259            Color color = Color.FromArgb(rgb & 0x007F7FFF);
260
261            richTextBox1.SelectionColor = color;
262            richTextBox1.AppendText(msg + Environment.NewLine);
263        }
264
265		//
266		// OnReadSocket
267		//
268		// Called by the .NET core when there is data to be read on a socket
269		//
270		// This is called from a worker thread by the .NET core
271		//
272		private void
273		OnReadSocket
274				(
275				IAsyncResult ar
276				)
277		{
278			try
279			{
280				int read = m_socket.EndReceive(ar);
281
282				if (read > 0)
283				{
284					String msg = Encoding.UTF8.GetString(m_buffer, 0, read);
285					Invoke(m_readMessageCallback, new Object[]{msg});
286				}
287
288				m_socket.BeginReceive(m_buffer, 0, BUFFER_SIZE, 0, new AsyncCallback(OnReadSocket), this);
289			}
290			catch
291			{
292			}
293		}
294
295
296		public SimpleChat()
297		{
298			//
299			// Required for Windows Form Designer support
300			//
301			InitializeComponent();
302
303            try
304            {
305                m_service = new DNSSDService();
306            }
307            catch
308            {
309                MessageBox.Show("Bonjour Service is not available", "Error");
310                Application.Exit();
311            }
312
313			//
314			// Associate event handlers with all the Bonjour events that the app is interested in.
315			//
316            m_eventManager = new DNSSDEventManager();
317            m_eventManager.ServiceRegistered += new _IDNSSDEvents_ServiceRegisteredEventHandler(this.ServiceRegistered);
318            m_eventManager.ServiceFound += new _IDNSSDEvents_ServiceFoundEventHandler(this.ServiceFound);
319            m_eventManager.ServiceLost += new _IDNSSDEvents_ServiceLostEventHandler(this.ServiceLost);
320            m_eventManager.ServiceResolved += new _IDNSSDEvents_ServiceResolvedEventHandler(this.ServiceResolved);
321            m_eventManager.QueryRecordAnswered += new _IDNSSDEvents_QueryRecordAnsweredEventHandler(this.QueryAnswered);
322            m_eventManager.OperationFailed += new _IDNSSDEvents_OperationFailedEventHandler(this.OperationFailed);
323
324			//
325			// Socket read handler
326			//
327			m_readMessageCallback = new ReadMessageCallback(OnReadMessage);
328
329			this.Load += new System.EventHandler(this.Form1_Load);
330
331			this.AcceptButton = button1;
332		}
333
334		/// <summary>
335		/// Clean up any resources being used.
336		/// </summary>
337		protected override void
338		Dispose( bool disposing )
339		{
340			if( disposing )
341			{
342				if (components != null)
343				{
344					components.Dispose();
345				}
346
347				if (m_registrar != null)
348				{
349					m_registrar.Stop();
350				}
351
352				if (m_browser != null)
353				{
354					m_browser.Stop();
355				}
356
357                if (m_resolver != null)
358                {
359                    m_resolver.Stop();
360                }
361
362                m_eventManager.ServiceFound -= new _IDNSSDEvents_ServiceFoundEventHandler(this.ServiceFound);
363                m_eventManager.ServiceLost -= new _IDNSSDEvents_ServiceLostEventHandler(this.ServiceLost);
364                m_eventManager.ServiceResolved -= new _IDNSSDEvents_ServiceResolvedEventHandler(this.ServiceResolved);
365                m_eventManager.QueryRecordAnswered -= new _IDNSSDEvents_QueryRecordAnsweredEventHandler(this.QueryAnswered);
366                m_eventManager.OperationFailed -= new _IDNSSDEvents_OperationFailedEventHandler(this.OperationFailed);
367			}
368			base.Dispose( disposing );
369		}
370
371		#region Windows Form Designer generated code
372		/// <summary>
373		/// Required method for Designer support - do not modify
374		/// the contents of this method with the code editor.
375		/// </summary>
376		private void InitializeComponent()
377		{
378			this.comboBox1 = new System.Windows.Forms.ComboBox();
379			this.textBox2 = new System.Windows.Forms.TextBox();
380			this.button1 = new System.Windows.Forms.Button();
381			this.label1 = new System.Windows.Forms.Label();
382			this.richTextBox1 = new System.Windows.Forms.RichTextBox();
383			this.SuspendLayout();
384			//
385			// comboBox1
386			//
387			this.comboBox1.Anchor = ((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
388				| System.Windows.Forms.AnchorStyles.Right);
389			this.comboBox1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
390			this.comboBox1.Location = new System.Drawing.Point(59, 208);
391			this.comboBox1.Name = "comboBox1";
392			this.comboBox1.Size = new System.Drawing.Size(224, 21);
393			this.comboBox1.Sorted = true;
394			this.comboBox1.TabIndex = 5;
395			this.comboBox1.SelectedIndexChanged += new System.EventHandler(this.comboBox1_SelectedIndexChanged);
396			//
397			// textBox2
398			//
399			this.textBox2.Anchor = ((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
400				| System.Windows.Forms.AnchorStyles.Right);
401			this.textBox2.Location = new System.Drawing.Point(8, 248);
402			this.textBox2.Name = "textBox2";
403			this.textBox2.ScrollBars = System.Windows.Forms.ScrollBars.Horizontal;
404			this.textBox2.Size = new System.Drawing.Size(192, 20);
405			this.textBox2.TabIndex = 2;
406			this.textBox2.Text = "";
407			this.textBox2.TextChanged += new System.EventHandler(this.textBox2_TextChanged);
408			//
409			// button1
410			//
411			this.button1.Anchor = (System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right);
412			this.button1.Enabled = false;
413			this.button1.Location = new System.Drawing.Point(208, 248);
414			this.button1.Name = "button1";
415			this.button1.TabIndex = 3;
416			this.button1.Text = "Send";
417			this.button1.Click += new System.EventHandler(this.button1_Click);
418			//
419			// label1
420			//
421			this.label1.Anchor = (System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left);
422			this.label1.Location = new System.Drawing.Point(8, 210);
423			this.label1.Name = "label1";
424			this.label1.Size = new System.Drawing.Size(48, 16);
425			this.label1.TabIndex = 4;
426			this.label1.Text = "Talk To:";
427			//
428			// richTextBox1
429			//
430			this.richTextBox1.Anchor = (((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
431				| System.Windows.Forms.AnchorStyles.Left)
432				| System.Windows.Forms.AnchorStyles.Right);
433			this.richTextBox1.Location = new System.Drawing.Point(8, 8);
434			this.richTextBox1.Name = "richTextBox1";
435			this.richTextBox1.ReadOnly = true;
436			this.richTextBox1.Size = new System.Drawing.Size(272, 184);
437			this.richTextBox1.TabIndex = 1;
438			this.richTextBox1.Text = "";
439			//
440			// Form1
441			//
442			this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
443			this.ClientSize = new System.Drawing.Size(292, 273);
444			this.Controls.AddRange(new System.Windows.Forms.Control[] {
445																		  this.richTextBox1,
446																		  this.label1,
447																		  this.button1,
448																		  this.textBox2,
449																		  this.comboBox1});
450			this.Name = "Form1";
451			this.Text = "SimpleChat.NET";
452			this.ResumeLayout(false);
453
454		}
455		#endregion
456
457		private void Form1_Load(object sender, EventArgs e)
458		{
459			IPEndPoint localEP = new IPEndPoint(System.Net.IPAddress.Any, 0);
460
461			//
462			// create the socket and bind to INADDR_ANY
463			//
464			m_socket	= new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
465			m_socket.Bind(localEP);
466			localEP = (IPEndPoint) m_socket.LocalEndPoint;
467
468			//
469			// start asynchronous read
470			//
471			m_socket.BeginReceive(m_buffer, 0, BUFFER_SIZE, 0, new AsyncCallback(this.OnReadSocket), this);
472
473			try
474			{
475				//
476				// start the register and browse operations
477				//
478				m_registrar	=	m_service.Register( 0, 0, System.Environment.UserName, "_p2pchat._udp", null, null, ( ushort ) localEP.Port, null, m_eventManager );
479			}
480			catch
481			{
482				MessageBox.Show("Bonjour service is not available", "Error");
483				Application.Exit();
484			}
485		}
486
487		/// <summary>
488		/// The main entry point for the application.
489		/// </summary>
490		[STAThread]
491		static void Main()
492		{
493			Application.Run(new SimpleChat());
494		}
495
496		//
497		// send the message to a peer
498		//
499		private void button1_Click(object sender, System.EventArgs e)
500		{
501			PeerData peer = (PeerData) comboBox1.SelectedItem;
502
503			String message = m_name + ": " + textBox2.Text;
504
505			Byte[] bytes = Encoding.UTF8.GetBytes(message);
506
507            IPEndPoint endPoint = new IPEndPoint( peer.Address, peer.Port );
508
509
510
511            m_socket.SendTo(bytes, endPoint);
512
513			richTextBox1.SelectionColor = Color.Black;
514
515			richTextBox1.AppendText(textBox2.Text + "\n");
516
517			textBox2.Text = "";
518		}
519
520		//
521		// called when typing in message box
522		//
523		private void textBox2_TextChanged(object sender, System.EventArgs e)
524		{
525			PeerData peer = (PeerData) comboBox1.SelectedItem;
526            button1.Enabled = ((peer.Address != null) && (textBox2.Text.Length > 0));
527		}
528
529		//
530		// called when peer target changes
531		//
532		/// <summary>
533		/// </summary>
534		/// <param name="sender"></param>
535		/// <param name="e"></param>
536		private void comboBox1_SelectedIndexChanged(object sender, System.EventArgs e)
537		{
538			PeerData peer = (PeerData) comboBox1.SelectedItem;
539
540			try
541			{
542				m_resolver = m_service.Resolve(0, peer.InterfaceIndex, peer.Name, peer.Type, peer.Domain, m_eventManager);
543			}
544			catch
545			{
546				MessageBox.Show("Unable to Resolve service", "Error");
547				Application.Exit();
548			}
549		}
550	}
551
552    //
553    // PeerData
554    //
555    // Holds onto the information associated with a peer on the network
556    //
557    public class PeerData
558    {
559        public uint InterfaceIndex;
560        public String Name;
561        public String Type;
562        public String Domain;
563        public IPAddress Address;
564        public int Port;
565
566        public override String
567        ToString()
568        {
569            return Name;
570        }
571
572        public override bool
573        Equals(object other)
574        {
575            bool result = false;
576
577            if (other != null)
578            {
579                if ((object)this == other)
580                {
581                    result = true;
582                }
583                else if (other is PeerData)
584                {
585                    PeerData otherPeerData = (PeerData)other;
586
587                    result = (this.Name == otherPeerData.Name);
588                }
589            }
590
591            return result;
592        }
593
594
595        public override int
596        GetHashCode()
597        {
598            return Name.GetHashCode();
599        }
600    };
601}
602