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.Collections;
24using System.Net;
25using System.Runtime.InteropServices;
26using System.Text;
27using Mono.Unix;
28
29namespace Avahi
30{
31
32    internal delegate void ServiceResolverCallback (IntPtr resolver, int iface, Protocol proto,
33                                                    ResolverEvent revent, IntPtr name, IntPtr type,
34                                                    IntPtr domain, IntPtr host, IntPtr address,
35                                                    UInt16 port, IntPtr txt, LookupResultFlags flags,
36                                                    IntPtr userdata);
37
38    public class ServiceResolver : ResolverBase, IDisposable
39    {
40        private IntPtr handle;
41        private ServiceInfo currentInfo;
42        private Client client;
43        private int iface;
44        private Protocol proto;
45        private string name;
46        private string type;
47        private string domain;
48        private Protocol aproto;
49        private LookupFlags flags;
50        private ServiceResolverCallback cb;
51
52        private ArrayList foundListeners = new ArrayList ();
53        private ArrayList timeoutListeners = new ArrayList ();
54
55        [DllImport ("avahi-client")]
56        private static extern IntPtr avahi_service_resolver_new (IntPtr client, int iface, Protocol proto,
57                                                                 byte[] name, byte[] type, byte[] domain,
58                                                                 Protocol aproto, LookupFlags flags,
59                                                                 ServiceResolverCallback cb,
60                                                                 IntPtr userdata);
61
62        [DllImport ("avahi-common")]
63        private static extern IntPtr avahi_string_list_get_next (IntPtr list);
64
65        [DllImport ("avahi-common")]
66        private static extern IntPtr avahi_string_list_get_text (IntPtr list);
67
68        [DllImport ("avahi-common")]
69        private static extern int avahi_string_list_get_size (IntPtr list);
70
71        [DllImport ("avahi-client")]
72        private static extern void avahi_service_resolver_free (IntPtr handle);
73
74        public event ServiceInfoHandler Found
75        {
76            add {
77                foundListeners.Add (value);
78                Start ();
79            }
80            remove {
81                foundListeners.Remove (value);
82                Stop (false);
83            }
84        }
85
86        public event EventHandler Timeout
87        {
88            add {
89                timeoutListeners.Add (value);
90                Start ();
91            }
92            remove {
93                timeoutListeners.Remove (value);
94                Stop (false);
95            }
96        }
97
98        public ServiceInfo Service
99        {
100            get { return currentInfo; }
101        }
102
103        public ServiceResolver (Client client, string name, string type, string domain) : this (client, -1,
104                                                                                                Protocol.Unspecified,
105                                                                                                name, type, domain,
106                                                                                                Protocol.Unspecified,
107                                                                                                LookupFlags.None)
108        {
109        }
110
111        public ServiceResolver (Client client, ServiceInfo service) : this (client, service.NetworkInterface,
112                                                                            service.Protocol, service.Name,
113                                                                            service.ServiceType, service.Domain,
114                                                                            Protocol.Unspecified,
115                                                                            GetLookupFlags (service.Flags))
116        {
117        }
118
119        public ServiceResolver (Client client, int iface, Protocol proto, string name,
120                                string type, string domain, Protocol aproto, LookupFlags flags)
121        {
122            this.client = client;
123            this.iface = iface;
124            this.proto = proto;
125            this.name = name;
126            this.type = type;
127            this.domain = domain;
128            this.aproto = aproto;
129            this.flags = flags;
130            cb = OnServiceResolverCallback;
131        }
132
133        ~ServiceResolver ()
134        {
135            Dispose ();
136        }
137
138        public void Dispose ()
139        {
140            Stop (true);
141        }
142
143        private void Start ()
144        {
145            if (client.Handle == IntPtr.Zero || handle != IntPtr.Zero ||
146                (foundListeners.Count == 0 && timeoutListeners.Count == 0))
147                return;
148
149            lock (client) {
150                handle = avahi_service_resolver_new (client.Handle, iface, proto,
151                                                     Utility.StringToBytes (name), Utility.StringToBytes (type),
152                                                     Utility.StringToBytes (domain), aproto, flags, cb, IntPtr.Zero);
153
154                if (handle == IntPtr.Zero)
155                    client.ThrowError ();
156            }
157        }
158
159        private void Stop (bool force)
160        {
161            if (client.Handle != IntPtr.Zero && handle != IntPtr.Zero &&
162                (force || (foundListeners.Count == 0 && timeoutListeners.Count == 0))) {
163
164                lock (client) {
165                    avahi_service_resolver_free (handle);
166                    handle = IntPtr.Zero;
167                }
168            }
169        }
170
171        private void OnServiceResolverCallback (IntPtr resolver, int iface, Protocol proto,
172                                                ResolverEvent revent, IntPtr name, IntPtr type,
173                                                IntPtr domain, IntPtr host, IntPtr address,
174                                                UInt16 port, IntPtr txt, LookupResultFlags flags,
175                                                IntPtr userdata)
176        {
177            ServiceInfo info;
178            info.NetworkInterface = iface;
179            info.Protocol = proto;
180            info.Domain = Utility.PtrToString (domain);
181            info.ServiceType = Utility.PtrToString (type);
182            info.Name = Utility.PtrToString (name);
183            info.HostName = Utility.PtrToString (host);
184            info.Address = Utility.PtrToAddress (address);
185            info.Port = port;
186
187            if (proto == Protocol.IPv6)
188              info.Address.ScopeId = iface;
189
190            ArrayList txtlist = new ArrayList ();
191            for (IntPtr l = txt; l != IntPtr.Zero; l = avahi_string_list_get_next (l)) {
192                IntPtr buf = avahi_string_list_get_text (l);
193                int len = avahi_string_list_get_size (l);
194
195                byte[] txtbuf = new byte[len];
196                Marshal.Copy (buf, txtbuf, 0, len);
197                txtlist.Add (txtbuf);
198            }
199
200            info.Text = (byte[][]) txtlist.ToArray (typeof (byte[]));
201            info.Flags = flags;
202
203            switch (revent) {
204            case ResolverEvent.Found:
205                currentInfo = info;
206
207                foreach (ServiceInfoHandler handler in foundListeners)
208                    handler (this, new ServiceInfoArgs (info));
209                break;
210            case ResolverEvent.Failure:
211                EmitFailure (client.LastError);
212                break;
213            }
214        }
215
216        private static LookupFlags GetLookupFlags (LookupResultFlags rflags) {
217            LookupFlags ret = LookupFlags.None;
218
219            if ((rflags & LookupResultFlags.Multicast) > 0)
220                ret |= LookupFlags.UseMulticast;
221            if ((rflags & LookupResultFlags.WideArea) > 0)
222                ret |= LookupFlags.UseWideArea;
223
224            return ret;
225        }
226    }
227}
228