1/* $Id$ */
2
3/***
4  This file is part of avahi.
5
6  avahi is free software; you can redistribute it and/or modify it
7  under the terms of the GNU Lesser General Public License as
8  published by the Free Software Foundation; either version 2.1 of the
9  License, or (at your option) any later version.
10
11  avahi is distributed in the hope that it will be useful, but WITHOUT
12  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
14  Public License for more details.
15
16  You should have received a copy of the GNU Lesser General Public
17  License along with avahi; if not, write to the Free Software
18  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19  USA.
20***/
21
22
23using System;
24using System.Threading;
25using System.Collections;
26using System.Runtime.InteropServices;
27using Mono.Unix;
28using Mono.Unix.Native;
29
30using Stdlib = Mono.Unix.Native.Stdlib;
31
32namespace Avahi
33{
34    internal enum ResolverEvent {
35        Found,
36        Failure
37    }
38
39    internal enum BrowserEvent {
40        Added,
41        Removed,
42        CacheExhausted,
43        AllForNow,
44        Failure
45    }
46
47    internal delegate int PollCallback (IntPtr ufds, uint nfds, int timeout);
48    internal delegate void ClientCallback (IntPtr client, ClientState state, IntPtr userData);
49
50    public delegate void ClientStateHandler (object o, ClientStateArgs state);
51
52    public class ClientStateArgs : EventArgs
53    {
54        private ClientState state;
55        private ErrorCode error;
56
57        public ClientState State
58        {
59            get { return state; }
60        }
61
62        public ErrorCode Error
63        {
64            get { return error; }
65        }
66
67        public ClientStateArgs (ClientState state, ErrorCode error)
68        {
69            this.state = state;
70            this.error = error;
71        }
72    }
73
74    public enum Protocol {
75        Unspecified = -1,
76        IPv4 = 0,
77        IPv6 = 1
78    }
79
80    internal enum ServerState {
81        Invalid,
82        Registering,
83        Running,
84        Collision
85    }
86
87    public enum ClientState {
88        Registering = ServerState.Registering,
89        Running = ServerState.Running,
90        Collision = ServerState.Collision,
91        Failure = 100,
92        Connecting = 101
93    }
94
95    [Flags]
96    public enum LookupFlags {
97        None = 0,
98        UseWideArea = 1,
99        UseMulticast = 2,
100	NoTxt = 4,
101        NoAddress = 8
102    }
103
104    [Flags]
105    public enum LookupResultFlags {
106        None = 0,
107        Cached = 1,
108        WideArea = 2,
109        Multicast = 4,
110        Local = 8,
111        OurOwn = 16,
112    }
113
114    [Flags]
115    public enum ClientFlags {
116        None = 0,
117        IgnoreUserConfig = 1,
118        NoFail = 2
119    }
120
121    public class Client : IDisposable
122    {
123        private IntPtr handle;
124        private ClientCallback cb;
125        private PollCallback pollcb;
126        private IntPtr spoll;
127
128        private Thread thread;
129
130        [DllImport ("avahi-client")]
131        private static extern IntPtr avahi_client_new (IntPtr poll, ClientFlags flags, ClientCallback handler,
132                                                       IntPtr userData, out int error);
133
134        [DllImport ("avahi-client")]
135        private static extern void avahi_client_free (IntPtr handle);
136
137        [DllImport ("avahi-client")]
138        private static extern IntPtr avahi_client_get_version_string (IntPtr handle);
139
140        [DllImport ("avahi-client")]
141        private static extern IntPtr avahi_client_get_host_name (IntPtr handle);
142
143        [DllImport ("avahi-client")]
144        private static extern IntPtr avahi_client_get_domain_name (IntPtr handle);
145
146        [DllImport ("avahi-client")]
147        private static extern IntPtr avahi_client_get_host_name_fqdn (IntPtr handle);
148
149        [DllImport ("avahi-client")]
150        private static extern ClientState avahi_client_get_state (IntPtr handle);
151
152        [DllImport ("avahi-client")]
153        private static extern int avahi_client_errno (IntPtr handle);
154
155        [DllImport ("avahi-common")]
156        private static extern IntPtr avahi_simple_poll_new ();
157
158        [DllImport ("avahi-common")]
159        private static extern IntPtr avahi_simple_poll_get (IntPtr spoll);
160
161        [DllImport ("avahi-common")]
162        private static extern void avahi_simple_poll_free (IntPtr spoll);
163
164        [DllImport ("avahi-common")]
165        private static extern int avahi_simple_poll_loop (IntPtr spoll);
166
167        [DllImport ("avahi-common")]
168        private static extern void avahi_simple_poll_set_func (IntPtr spoll, PollCallback cb);
169
170        [DllImport ("avahi-common")]
171        private static extern void avahi_simple_poll_quit (IntPtr spoll);
172
173        [DllImport ("avahi-client")]
174        private static extern uint avahi_client_get_local_service_cookie (IntPtr client);
175
176        [DllImport ("avahi-common")]
177        private static extern int avahi_service_name_join (IntPtr buf, int len, byte[] name, byte[] type,
178                                                           byte[] domain);
179
180        [DllImport ("avahi-common")]
181        private static extern int avahi_service_name_split (byte[] service, IntPtr name, int name_len,
182                                                            IntPtr type, int type_len,
183                                                            IntPtr domain, int domain_len);
184
185
186        [DllImport ("libc")]
187        private static extern int poll(IntPtr ufds, uint nfds, int timeout);
188
189        public event ClientStateHandler StateChanged;
190
191        internal IntPtr Handle
192        {
193            get { return handle; }
194        }
195
196        public string Version
197        {
198            get {
199                lock (this) {
200                    return Utility.PtrToString (avahi_client_get_version_string (handle));
201                }
202            }
203        }
204
205        public string HostName
206        {
207            get {
208                lock (this) {
209                    return Utility.PtrToString (avahi_client_get_host_name (handle));
210                }
211            }
212        }
213
214        public string DomainName
215        {
216            get {
217                lock (this) {
218                    return Utility.PtrToString (avahi_client_get_domain_name (handle));
219                }
220            }
221        }
222
223        public string HostNameFqdn
224        {
225            get {
226                lock (this) {
227                    return Utility.PtrToString (avahi_client_get_host_name_fqdn (handle));
228                }
229            }
230        }
231
232        public ClientState State
233        {
234            get {
235                lock (this) {
236                    return (ClientState) avahi_client_get_state (handle);
237                }
238            }
239        }
240
241        public uint LocalServiceCookie
242        {
243            get {
244                lock (this) {
245                    return avahi_client_get_local_service_cookie (handle);
246                }
247            }
248        }
249
250        internal ErrorCode LastError
251        {
252            get {
253                lock (this) {
254                    return (ErrorCode) avahi_client_errno (handle);
255                }
256            }
257        }
258
259        public Client (ClientFlags flags)
260        {
261            spoll = avahi_simple_poll_new ();
262
263            pollcb = OnPollCallback;
264            avahi_simple_poll_set_func (spoll, pollcb);
265            IntPtr poll = avahi_simple_poll_get (spoll);
266            cb = OnClientCallback;
267
268            int error;
269            handle = avahi_client_new (poll, flags, cb, IntPtr.Zero, out error);
270            if (error != 0)
271                throw new ClientException (error);
272
273            thread = new Thread (PollLoop);
274            thread.IsBackground = true;
275            thread.Start ();
276        }
277
278        public Client () : this (ClientFlags.None) {
279        }
280
281        ~Client ()
282        {
283            Dispose ();
284        }
285
286        public void Dispose ()
287        {
288            if (handle != IntPtr.Zero) {
289                lock (this) {
290                    avahi_client_free (handle);
291                    handle = IntPtr.Zero;
292
293                    avahi_simple_poll_quit (spoll);
294                    Monitor.Wait (this);
295
296                    avahi_simple_poll_free (spoll);
297                }
298            }
299        }
300
301        public static string JoinServiceName (string name, string type, string domain)
302        {
303            int len = 4 * (name.Length + type.Length + domain.Length) + 4;
304            IntPtr buf = Stdlib.malloc ((ulong) len);
305
306            int ret = avahi_service_name_join (buf, len,
307                                               Utility.StringToBytes (name),
308                                               Utility.StringToBytes (type),
309                                               Utility.StringToBytes (domain));
310
311            if (ret < 0) {
312                Utility.Free (buf);
313                return null; // FIXME, should throw exception
314            }
315
316            string service = Utility.PtrToString (buf);
317            Utility.Free (buf);
318
319            return service;
320        }
321
322        public static void SplitServiceName (string service, out string name, out string type, out string domain)
323        {
324            int len = 1024;
325
326            IntPtr namePtr = Stdlib.malloc ((ulong) len);
327            IntPtr typePtr = Stdlib.malloc ((ulong) len);
328            IntPtr domainPtr = Stdlib.malloc ((ulong) len);
329
330            int ret = avahi_service_name_split (Utility.StringToBytes (service), namePtr, len, typePtr, len,
331                                                domainPtr, len);
332
333            if (ret < 0) {
334                Utility.Free (namePtr);
335                Utility.Free (typePtr);
336                Utility.Free (domainPtr);
337
338                name = null;
339                type = null;
340                domain = null;
341                return;
342            }
343
344            name = Utility.PtrToString (namePtr);
345            type = Utility.PtrToString (typePtr);
346            domain = Utility.PtrToString (domainPtr);
347
348            Utility.Free (namePtr);
349            Utility.Free (typePtr);
350            Utility.Free (domainPtr);
351        }
352
353        internal void ThrowError ()
354        {
355            ErrorCode error = LastError;
356
357            if (error != ErrorCode.Ok)
358                throw new ClientException (error);
359        }
360
361        private void OnClientCallback (IntPtr client, ClientState state, IntPtr userData)
362        {
363            if (StateChanged != null)
364                StateChanged (this, new ClientStateArgs (state, LastError));
365        }
366
367        private int OnPollCallback (IntPtr ufds, uint nfds, int timeout) {
368            Monitor.Exit (this);
369            int result = poll (ufds, nfds, timeout);
370            Monitor.Enter (this);
371            return result;
372        }
373
374        private void PollLoop () {
375            try {
376                lock (this) {
377                    avahi_simple_poll_loop (spoll);
378                    Monitor.Pulse (this);
379                }
380            } catch (Exception e) {
381                Console.Error.WriteLine ("Error in avahi-sharp event loop: " + e);
382            }
383        }
384    }
385}
386