1/*
2 * ga-service-resolver.c - Source for GaServiceResolver
3 * Copyright (C) 2006-2007 Collabora Ltd.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18 */
19
20#ifdef HAVE_CONFIG_H
21#include <config.h>
22#endif
23
24#include <stdio.h>
25#include <stdlib.h>
26
27#include "ga-service-resolver.h"
28#include "signals-marshal.h"
29
30#include "ga-error.h"
31
32#include "ga-enums.h"
33#include "ga-enums-enumtypes.h"
34
35#include <avahi-client/lookup.h>
36
37G_DEFINE_TYPE(GaServiceResolver, ga_service_resolver, G_TYPE_OBJECT)
38
39/* signal enum */
40enum {
41    FOUND,
42    FAILURE,
43    LAST_SIGNAL
44};
45
46static guint signals[LAST_SIGNAL] = { 0 };
47
48/* properties */
49enum {
50    PROP_PROTOCOL = 1,
51    PROP_IFINDEX,
52    PROP_NAME,
53    PROP_TYPE,
54    PROP_DOMAIN,
55    PROP_FLAGS,
56    PROP_APROTOCOL
57};
58
59/* private structure */
60typedef struct _GaServiceResolverPrivate GaServiceResolverPrivate;
61
62struct _GaServiceResolverPrivate {
63    GaClient *client;
64    AvahiServiceResolver *resolver;
65    AvahiIfIndex interface;
66    AvahiProtocol protocol;
67    AvahiAddress address;
68    uint16_t port;
69    char *name;
70    char *type;
71    char *domain;
72    AvahiProtocol aprotocol;
73    AvahiLookupFlags flags;
74    gboolean dispose_has_run;
75};
76
77#define GA_SERVICE_RESOLVER_GET_PRIVATE(o)     (G_TYPE_INSTANCE_GET_PRIVATE ((o), GA_TYPE_SERVICE_RESOLVER, GaServiceResolverPrivate))
78
79static void ga_service_resolver_init(GaServiceResolver * obj) {
80    GaServiceResolverPrivate *priv = GA_SERVICE_RESOLVER_GET_PRIVATE(obj);
81
82    /* allocate any data required by the object here */
83    priv->client = NULL;
84    priv->resolver = NULL;
85    priv->name = NULL;
86    priv->type = NULL;
87    priv->domain = NULL;
88    priv->port = 0;
89}
90
91static void ga_service_resolver_dispose(GObject * object);
92static void ga_service_resolver_finalize(GObject * object);
93
94static void ga_service_resolver_set_property(GObject * object,
95                                 guint property_id,
96                                 const GValue * value, GParamSpec * pspec) {
97    GaServiceResolver *resolver = GA_SERVICE_RESOLVER(object);
98    GaServiceResolverPrivate *priv =
99            GA_SERVICE_RESOLVER_GET_PRIVATE(resolver);
100
101    g_assert(priv->resolver == NULL);
102    switch (property_id) {
103        case PROP_PROTOCOL:
104            priv->protocol = g_value_get_enum(value);
105            break;
106        case PROP_APROTOCOL:
107            priv->aprotocol = g_value_get_enum(value);
108            break;
109        case PROP_IFINDEX:
110            priv->interface = g_value_get_int(value);
111            break;
112        case PROP_NAME:
113            priv->name = g_strdup(g_value_get_string(value));
114            break;
115        case PROP_TYPE:
116            priv->type = g_strdup(g_value_get_string(value));
117            break;
118        case PROP_DOMAIN:
119            priv->domain = g_strdup(g_value_get_string(value));
120            break;
121        case PROP_FLAGS:
122            priv->flags = g_value_get_enum(value);
123            break;
124        default:
125            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
126            break;
127    }
128}
129
130static void ga_service_resolver_get_property(GObject * object,
131                                 guint property_id,
132                                 GValue * value, GParamSpec * pspec) {
133    GaServiceResolver *resolver = GA_SERVICE_RESOLVER(object);
134    GaServiceResolverPrivate *priv =
135            GA_SERVICE_RESOLVER_GET_PRIVATE(resolver);
136
137    switch (property_id) {
138        case PROP_APROTOCOL:
139            g_value_set_enum(value, priv->aprotocol);
140            break;
141        case PROP_PROTOCOL:
142            g_value_set_enum(value, priv->protocol);
143            break;
144        case PROP_IFINDEX:
145            g_value_set_int(value, priv->interface);
146            break;
147        case PROP_NAME:
148            g_value_set_string(value, priv->name);
149            break;
150        case PROP_TYPE:
151            g_value_set_string(value, priv->type);
152            break;
153        case PROP_DOMAIN:
154            g_value_set_string(value, priv->domain);
155            break;
156        case PROP_FLAGS:
157            g_value_set_enum(value, priv->flags);
158            break;
159        default:
160            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
161            break;
162    }
163}
164
165
166static void ga_service_resolver_class_init(GaServiceResolverClass *
167                               ga_service_resolver_class) {
168    GObjectClass *object_class = G_OBJECT_CLASS(ga_service_resolver_class);
169    GParamSpec *param_spec;
170
171    g_type_class_add_private(ga_service_resolver_class,
172                             sizeof (GaServiceResolverPrivate));
173
174    object_class->set_property = ga_service_resolver_set_property;
175    object_class->get_property = ga_service_resolver_get_property;
176
177    object_class->dispose = ga_service_resolver_dispose;
178    object_class->finalize = ga_service_resolver_finalize;
179
180    signals[FOUND] =
181            g_signal_new("found",
182                         G_OBJECT_CLASS_TYPE(ga_service_resolver_class),
183                         G_SIGNAL_RUN_LAST,
184                         0,
185                         NULL, NULL,
186                         _ga_signals_marshal_VOID__INT_ENUM_STRING_STRING_STRING_STRING_POINTER_INT_POINTER_INT,
187                         G_TYPE_NONE, 10,
188                         G_TYPE_INT,
189                         GA_TYPE_PROTOCOL,
190                         G_TYPE_STRING,
191                         G_TYPE_STRING,
192                         G_TYPE_STRING,
193                         G_TYPE_STRING,
194                         G_TYPE_POINTER,
195                         G_TYPE_INT,
196                         G_TYPE_POINTER, GA_TYPE_LOOKUP_RESULT_FLAGS);
197
198    signals[FAILURE] =
199            g_signal_new("failure",
200                         G_OBJECT_CLASS_TYPE(ga_service_resolver_class),
201                         G_SIGNAL_RUN_LAST,
202                         0,
203                         NULL, NULL,
204                         g_cclosure_marshal_VOID__POINTER,
205                         G_TYPE_NONE, 1, G_TYPE_POINTER);
206
207    param_spec = g_param_spec_enum("protocol", "Avahi protocol to resolve on",
208                                   "Avahi protocol to resolve on",
209                                   GA_TYPE_PROTOCOL,
210                                   GA_PROTOCOL_UNSPEC,
211                                   G_PARAM_READWRITE |
212                                   G_PARAM_STATIC_NAME |
213                                   G_PARAM_STATIC_BLURB);
214    g_object_class_install_property(object_class, PROP_PROTOCOL, param_spec);
215
216    param_spec = g_param_spec_enum("aprotocol", "Address protocol",
217                                   "Avahi protocol of the address to be resolved",
218                                   GA_TYPE_PROTOCOL,
219                                   GA_PROTOCOL_UNSPEC,
220                                   G_PARAM_READWRITE |
221                                   G_PARAM_STATIC_NAME |
222                                   G_PARAM_STATIC_BLURB);
223    g_object_class_install_property(object_class, PROP_APROTOCOL, param_spec);
224
225    param_spec = g_param_spec_int("interface", "interface index",
226                                  "Interface use for resolver",
227                                  AVAHI_IF_UNSPEC,
228                                  G_MAXINT,
229                                  AVAHI_IF_UNSPEC,
230                                  G_PARAM_READWRITE |
231                                  G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB);
232    g_object_class_install_property(object_class, PROP_IFINDEX, param_spec);
233
234    param_spec = g_param_spec_string("name", "service name",
235                                     "name to resolve",
236                                     NULL,
237                                     G_PARAM_READWRITE |
238                                     G_PARAM_STATIC_NAME |
239                                     G_PARAM_STATIC_BLURB);
240    g_object_class_install_property(object_class, PROP_NAME, param_spec);
241
242    param_spec = g_param_spec_string("type", "service type",
243                                     "Service type to browse for",
244                                     NULL,
245                                     G_PARAM_READWRITE |
246                                     G_PARAM_STATIC_NAME |
247                                     G_PARAM_STATIC_BLURB);
248    g_object_class_install_property(object_class, PROP_TYPE, param_spec);
249
250    param_spec = g_param_spec_string("domain", "service domain",
251                                     "Domain to browse in",
252                                     NULL,
253                                     G_PARAM_READWRITE |
254                                     G_PARAM_STATIC_NAME |
255                                     G_PARAM_STATIC_BLURB);
256    g_object_class_install_property(object_class, PROP_DOMAIN, param_spec);
257
258    param_spec = g_param_spec_enum("flags", "Lookup flags for the resolver",
259                                   "Resolver lookup flags",
260                                   GA_TYPE_LOOKUP_FLAGS,
261                                   GA_LOOKUP_NO_FLAGS,
262                                   G_PARAM_READWRITE |
263                                   G_PARAM_STATIC_NAME |
264                                   G_PARAM_STATIC_BLURB);
265    g_object_class_install_property(object_class, PROP_FLAGS, param_spec);
266}
267
268void ga_service_resolver_dispose(GObject * object) {
269    GaServiceResolver *self = GA_SERVICE_RESOLVER(object);
270    GaServiceResolverPrivate *priv = GA_SERVICE_RESOLVER_GET_PRIVATE(self);
271
272    if (priv->dispose_has_run)
273        return;
274
275    priv->dispose_has_run = TRUE;
276
277    if (priv->client)
278        g_object_unref(priv->client);
279    priv->client = NULL;
280
281    if (priv->resolver)
282        avahi_service_resolver_free(priv->resolver);
283    priv->resolver = NULL;
284
285    /* release any references held by the object here */
286
287    if (G_OBJECT_CLASS(ga_service_resolver_parent_class)->dispose)
288        G_OBJECT_CLASS(ga_service_resolver_parent_class)->dispose(object);
289}
290
291void ga_service_resolver_finalize(GObject * object) {
292    GaServiceResolver *self = GA_SERVICE_RESOLVER(object);
293    GaServiceResolverPrivate *priv = GA_SERVICE_RESOLVER_GET_PRIVATE(self);
294
295    /* free any data held directly by the object here */
296    g_free(priv->name);
297    priv->name = NULL;
298
299    g_free(priv->type);
300    priv->type = NULL;
301
302    g_free(priv->domain);
303    priv->domain = NULL;
304
305    G_OBJECT_CLASS(ga_service_resolver_parent_class)->finalize(object);
306}
307
308static void _avahi_service_resolver_cb(AVAHI_GCC_UNUSED AvahiServiceResolver * resolver,
309                           AvahiIfIndex interface,
310                           AvahiProtocol protocol,
311                           AvahiResolverEvent event,
312                           const char *name, const char *type,
313                           const char *domain, const char *host_name,
314                           const AvahiAddress * a,
315                           uint16_t port,
316                           AvahiStringList * txt,
317                           AvahiLookupResultFlags flags, void *userdata) {
318    GaServiceResolver *self = GA_SERVICE_RESOLVER(userdata);
319    GaServiceResolverPrivate *priv = GA_SERVICE_RESOLVER_GET_PRIVATE(self);
320
321    switch (event) {
322        case AVAHI_RESOLVER_FOUND:{
323                /* FIXME: Double check if this address is always the same */
324                priv->address = *a;
325                priv->port = port;
326                g_signal_emit(self, signals[FOUND], 0,
327                              interface, protocol,
328                              name, type,
329                              domain, host_name, a, port, txt, flags);
330                break;
331            }
332        case AVAHI_RESOLVER_FAILURE:{
333                GError *error;
334                int aerrno = avahi_client_errno(priv->client->avahi_client);
335                error = g_error_new(GA_ERROR, aerrno,
336                                    "Resolving failed: %s",
337                                    avahi_strerror(aerrno));
338                g_signal_emit(self, signals[FAILURE], 0, error);
339                g_error_free(error);
340                break;
341            }
342    }
343}
344
345
346GaServiceResolver *ga_service_resolver_new(AvahiIfIndex interface,
347                                           AvahiProtocol protocol,
348                                           const gchar * name,
349                                           const gchar * type,
350                                           const gchar * domain,
351                                           AvahiProtocol address_protocol,
352                                           GaLookupFlags flags) {
353    return g_object_new(GA_TYPE_SERVICE_RESOLVER, "interface", interface,
354                        "protocol", protocol, "name", name, "type", type,
355                        "domain", domain, "aprotocol", address_protocol,
356                        "flags", flags, NULL);
357}
358
359gboolean ga_service_resolver_attach(GaServiceResolver * resolver,
360                           GaClient * client, GError ** error) {
361    GaServiceResolverPrivate *priv =
362            GA_SERVICE_RESOLVER_GET_PRIVATE(resolver);
363
364    g_assert(client != NULL);
365    g_object_ref(client);
366
367    priv->client = client;
368
369    priv->resolver = avahi_service_resolver_new(client->avahi_client,
370                                                priv->interface,
371                                                priv->protocol,
372                                                priv->name,
373                                                priv->type, priv->domain,
374                                                priv->aprotocol,
375                                                priv->flags,
376                                                _avahi_service_resolver_cb,
377                                                resolver);
378    if (priv->resolver == NULL) {
379        if (error != NULL) {
380            int aerrno = avahi_client_errno(client->avahi_client);
381            *error = g_error_new(GA_ERROR, aerrno,
382                                 "Attaching group failed: %s",
383                                 avahi_strerror(aerrno));
384        }
385/*         printf("Failed to add resolver\n"); */
386        return FALSE;
387    }
388    return TRUE;
389}
390
391gboolean ga_service_resolver_get_address(GaServiceResolver * resolver,
392                                AvahiAddress * address, uint16_t * port) {
393    GaServiceResolverPrivate *priv =
394            GA_SERVICE_RESOLVER_GET_PRIVATE(resolver);
395    if (priv->port == 0) {
396/*         printf("PORT == 0\n"); */
397        return FALSE;
398    }
399
400    *address = priv->address;
401    *port = priv->port;
402    return TRUE;
403}
404