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
22using System;
23using System.Net;
24using System.Runtime.InteropServices;
25using System.Text;
26using Mono.Unix;
27
28namespace Avahi
29{
30
31    [Flags]
32    public enum PublishFlags {
33        None = 0,
34        Unique = 1,
35        NoProbe = 2,
36        NoAnnounce = 4,
37        AllowMultiple = 8,
38        NoReverse = 16,
39        NoCookie = 32,
40        Update = 64,
41        UseWideArea = 128,
42        UseMulticast = 256
43    }
44
45    public enum EntryGroupState {
46        Uncommited,
47        Registering,
48        Established,
49        Collision,
50        Failure
51    }
52
53    public class EntryGroupStateArgs : EventArgs
54    {
55        private EntryGroupState state;
56
57        public EntryGroupState State
58        {
59            get { return state; }
60        }
61
62        public EntryGroupStateArgs (EntryGroupState state)
63        {
64            this.state = state;
65        }
66    }
67
68    internal delegate void EntryGroupCallback (IntPtr group, EntryGroupState state, IntPtr userdata);
69    public delegate void EntryGroupStateHandler (object o, EntryGroupStateArgs args);
70
71    public class EntryGroup : IDisposable
72    {
73        private Client client;
74        private IntPtr handle;
75        private EntryGroupCallback cb;
76
77        [DllImport ("avahi-client")]
78        private static extern IntPtr avahi_entry_group_new (IntPtr client, EntryGroupCallback cb, IntPtr userdata);
79
80        [DllImport ("avahi-client")]
81        private static extern int avahi_entry_group_commit (IntPtr group);
82
83        [DllImport ("avahi-client")]
84        private static extern int avahi_entry_group_reset (IntPtr group);
85
86        [DllImport ("avahi-client")]
87        private static extern EntryGroupState avahi_entry_group_get_state (IntPtr group);
88
89        [DllImport ("avahi-client")]
90        private static extern bool avahi_entry_group_is_empty (IntPtr group);
91
92        [DllImport ("avahi-client")]
93        private static extern int avahi_entry_group_add_service_strlst (IntPtr group, int iface, Protocol proto,
94                                                                        PublishFlags flags, byte[] name, byte[] type,
95                                                                        byte[] domain, byte[] host, UInt16 port,
96                                                                        IntPtr strlst);
97
98        [DllImport ("avahi-client")]
99        private static extern int avahi_entry_group_update_service_strlst (IntPtr group, int iface, Protocol proto,
100                                                                           PublishFlags flags, byte[] name,
101                                                                           byte[] type, byte[] domain, IntPtr strlst);
102
103        [DllImport ("avahi-client")]
104        private static extern int avahi_entry_group_add_service_subtype (IntPtr group, int iface, Protocol proto,
105                                                                         PublishFlags flags, byte[] name, byte[] type,
106                                                                         byte[] domain, byte[] subtype);
107
108        [DllImport ("avahi-client")]
109        private static extern int avahi_entry_group_add_address (IntPtr group, int iface, Protocol proto,
110                                                                 PublishFlags flags, byte[] name, IntPtr address);
111
112
113        [DllImport ("avahi-client")]
114        private static extern int avahi_entry_group_add_record (IntPtr group, int iface, Protocol proto,
115                                                                PublishFlags flags, byte[] name, RecordClass clazz,
116                                                                RecordType type, uint ttl, byte[] rdata, int size);
117
118        [DllImport ("avahi-client")]
119        private static extern void avahi_entry_group_free (IntPtr group);
120
121        [DllImport ("avahi-common")]
122        private static extern IntPtr avahi_string_list_new (IntPtr txt);
123
124        [DllImport ("avahi-common")]
125        private static extern IntPtr avahi_string_list_add (IntPtr list, byte[] txt);
126
127        [DllImport ("avahi-common")]
128        private static extern void avahi_string_list_free (IntPtr list);
129
130        [DllImport ("avahi-common")]
131        private static extern IntPtr avahi_alternative_service_name (byte[] name);
132
133        public event EntryGroupStateHandler StateChanged;
134
135        public EntryGroupState State
136        {
137            get {
138                lock (client) {
139                    return avahi_entry_group_get_state (handle);
140                }
141            }
142        }
143
144        public bool IsEmpty
145        {
146            get {
147                lock (client) {
148                    return avahi_entry_group_is_empty (handle);
149                }
150            }
151        }
152
153        public EntryGroup (Client client)
154        {
155            this.client = client;
156            cb = OnEntryGroupCallback;
157
158            lock (client) {
159                handle = avahi_entry_group_new (client.Handle, cb, IntPtr.Zero);
160                if (handle == IntPtr.Zero)
161                    client.ThrowError ();
162            }
163        }
164
165        ~EntryGroup ()
166        {
167            Dispose ();
168        }
169
170        public void Dispose ()
171        {
172            if (client.Handle != IntPtr.Zero && handle != IntPtr.Zero) {
173                lock (client) {
174                    avahi_entry_group_free (handle);
175                    handle = IntPtr.Zero;
176                }
177            }
178        }
179
180        public void Commit ()
181        {
182            lock (client) {
183                if (avahi_entry_group_commit (handle) < 0)
184                    client.ThrowError ();
185            }
186        }
187
188        public void Reset ()
189        {
190            lock (client) {
191                if (avahi_entry_group_reset (handle) < 0)
192                    client.ThrowError ();
193            }
194        }
195
196        public void AddService (string name, string type, string domain,
197                                UInt16 port, params string[] txt)
198        {
199            AddService (PublishFlags.None, name, type, domain, port, txt);
200        }
201
202        public void AddService (PublishFlags flags, string name, string type, string domain,
203                                UInt16 port, params string[] txt)
204        {
205            AddService (-1, Protocol.Unspecified, flags, name, type, domain, null, port, txt);
206        }
207
208        public void AddService (int iface, Protocol proto, PublishFlags flags, string name, string type, string domain,
209                                string host, UInt16 port, params string[] txt)
210        {
211            IntPtr list = avahi_string_list_new (IntPtr.Zero);
212
213            if (txt != null) {
214                foreach (string item in txt) {
215                    list = avahi_string_list_add (list, Utility.StringToBytes (item));
216                }
217            }
218
219            AddService (iface, proto, flags, name, type, domain, host, port, list);
220        }
221
222        public void AddService (int iface, Protocol proto, PublishFlags flags, string name, string type, string domain,
223                                string host, UInt16 port, params byte[][] txt)
224        {
225            IntPtr list = avahi_string_list_new (IntPtr.Zero);
226
227            if (txt != null) {
228                foreach (byte[] item in txt) {
229                    list = avahi_string_list_add (list, item);
230                }
231            }
232
233            AddService (iface, proto, flags, name, type, domain, host, port, list);
234        }
235
236        private void AddService (int iface, Protocol proto, PublishFlags flags, string name, string type,
237                                 string domain, string host, UInt16 port, IntPtr list)
238        {
239            int ret;
240
241            lock (client) {
242                ret = avahi_entry_group_add_service_strlst (handle, iface, proto, flags,
243                                                            Utility.StringToBytes (name),
244                                                            Utility.StringToBytes (type),
245                                                            Utility.StringToBytes (domain),
246                                                            Utility.StringToBytes (host), port, list);
247            }
248
249            avahi_string_list_free (list);
250
251            if (ret < 0) {
252                client.ThrowError ();
253            }
254        }
255
256        public void UpdateService (string name, string type, string domain, params string[] txt)
257        {
258            UpdateService (-1, Protocol.Unspecified, PublishFlags.None, name, type, domain, txt);
259        }
260
261        public void UpdateService (int iface, Protocol proto, PublishFlags flags, string name, string type,
262                                   string domain, params string[] txt)
263        {
264            IntPtr list = avahi_string_list_new (IntPtr.Zero);
265
266            if (txt != null) {
267                foreach (string item in txt) {
268                    list = avahi_string_list_add (list, Utility.StringToBytes (item));
269                }
270            }
271
272            UpdateService (iface, proto, flags, name, type, domain, list);
273        }
274
275        public void UpdateService (int iface, Protocol proto, PublishFlags flags, string name, string type,
276                                   string domain, params byte[][] txt)
277        {
278            IntPtr list = avahi_string_list_new (IntPtr.Zero);
279
280            if (txt != null) {
281                foreach (byte[] item in txt) {
282                    list = avahi_string_list_add (list, item);
283                }
284            }
285
286            UpdateService (iface, proto, flags, name, type, domain, list);
287        }
288
289        private void UpdateService (int iface, Protocol proto, PublishFlags flags, string name, string type,
290                                    string domain, IntPtr list)
291        {
292            lock (client) {
293                int ret = avahi_entry_group_update_service_strlst (handle, iface, proto, flags,
294                                                                   Utility.StringToBytes (name),
295                                                                   Utility.StringToBytes (type),
296                                                                   Utility.StringToBytes (domain),
297                                                                   list);
298
299                avahi_string_list_free (list);
300
301                if (ret < 0) {
302                    client.ThrowError ();
303                }
304            }
305        }
306
307        public void AddServiceSubtype (string name, string type, string domain, string subtype)
308        {
309            AddServiceSubtype (-1, Protocol.Unspecified, PublishFlags.None, name, type, domain, subtype);
310        }
311
312        public void AddServiceSubtype (int iface, Protocol proto, PublishFlags flags, string name,
313                                       string type, string domain, string subtype)
314        {
315            lock (client) {
316                int ret = avahi_entry_group_add_service_subtype (handle, iface, proto, flags,
317                                                                 Utility.StringToBytes (name),
318                                                                 Utility.StringToBytes (type),
319                                                                 Utility.StringToBytes (domain),
320                                                                 Utility.StringToBytes (subtype));
321
322                if (ret < 0) {
323                    client.ThrowError ();
324                }
325            }
326        }
327
328        public void AddAddress (string name, IPAddress address)
329        {
330            AddAddress (-1, Protocol.Unspecified, PublishFlags.None, name, address);
331        }
332
333        public void AddAddress (int iface, Protocol proto, PublishFlags flags, string name, IPAddress address)
334        {
335            IntPtr addressPtr = Utility.AddressToPtr (address);
336
337            lock (client) {
338                int ret = avahi_entry_group_add_address (handle, iface, proto, flags,
339                                                         Utility.StringToBytes (name), addressPtr);
340
341                Utility.Free (addressPtr);
342
343                if (ret < 0) {
344                    client.ThrowError ();
345                }
346            }
347        }
348
349        public void AddRecord (string name, RecordClass clazz, RecordType type, uint ttl, byte[] rdata, int length)
350        {
351            AddRecord (-1, Protocol.Unspecified, PublishFlags.None, name, clazz, type, ttl, rdata, length);
352        }
353
354        public void AddRecord (int iface, Protocol proto, PublishFlags flags, string name,
355                               RecordClass clazz, RecordType type, uint ttl, byte[] rdata, int length)
356        {
357            lock (client) {
358                int ret = avahi_entry_group_add_record (handle, iface, proto, flags,
359                                                        Utility.StringToBytes (name),
360                                                        clazz, type, ttl, rdata, length);
361
362                if (ret < 0) {
363                    client.ThrowError ();
364                }
365            }
366        }
367
368        public static string GetAlternativeServiceName (string name) {
369            return Utility.PtrToStringFree (avahi_alternative_service_name (Utility.StringToBytes (name)));
370        }
371
372        private void OnEntryGroupCallback (IntPtr group, EntryGroupState state, IntPtr userdata)
373        {
374            if (StateChanged != null)
375                StateChanged (this, new EntryGroupStateArgs (state));
376        }
377    }
378}
379