1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
3 * Copyright (C) 2005 Charles Schmidt <cschmidt2@emich.edu>
4 * Copyright (C) 2006 William Jon McCann <mccann@jhu.edu>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
29 #include <glib/gi18n.h>
30 #include <glib-object.h>
32 #include <libgnomevfs/gnome-vfs-address.h>
33 #include <libgnomevfs/gnome-vfs-resolve.h>
35 /* stupid howl includes howl_config.h */
40 #include "rb-daap-mdns-browser.h"
41 #include "rb-marshal.h"
44 static void rb_daap_mdns_browser_class_init (RBDaapMdnsBrowserClass
*klass
);
45 static void rb_daap_mdns_browser_init (RBDaapMdnsBrowser
*browser
);
46 static void rb_daap_mdns_browser_finalize (GObject
*object
);
48 #define RB_DAAP_MDNS_BROWSER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_DAAP_MDNS_BROWSER, RBDaapMdnsBrowserPrivate))
50 struct RBDaapMdnsBrowserPrivate
52 sw_discovery
*discovery
;
53 sw_discovery_oid
*oid
;
55 GnomeVFSAddress
*local_address
;
70 static guint signals
[LAST_SIGNAL
] = { 0, };
72 G_DEFINE_TYPE (RBDaapMdnsBrowser
, rb_daap_mdns_browser
, G_TYPE_OBJECT
)
74 static gpointer browser_object
= NULL
;
77 rb_daap_mdns_browser_error_quark (void)
79 static GQuark quark
= 0;
81 quark
= g_quark_from_static_string ("rb_daap_mdns_browser_error");
87 howl_in_cb (GIOChannel
*io_channel
,
88 GIOCondition condition
,
89 RBDaapMdnsBrowser
*browser
)
93 if (sw_discovery_salt (*browser
->priv
->discovery
, &salt
) == SW_OKAY
) {
95 sw_discovery_read_socket (*browser
->priv
->discovery
);
96 sw_salt_unlock (salt
);
103 howl_client_init (RBDaapMdnsBrowser
*browser
)
109 browser
->priv
->discovery
= g_new0 (sw_discovery
, 1);
110 result
= sw_discovery_init (browser
->priv
->discovery
);
112 if (result
!= SW_OKAY
) {
113 g_free (browser
->priv
->discovery
);
114 browser
->priv
->discovery
= NULL
;
118 fd
= sw_discovery_socket (*browser
->priv
->discovery
);
120 channel
= g_io_channel_unix_new (fd
);
121 browser
->priv
->watch_id
= g_io_add_watch (channel
, G_IO_IN
, (GIOFunc
)howl_in_cb
, browser
);
122 g_io_channel_unref (channel
);
126 host_is_local (RBDaapMdnsBrowser
*browser
,
129 GnomeVFSAddress
*remote
;
134 if (browser
->priv
->local_address
== NULL
) {
135 g_warning ("Unable to resolve address");
139 remote
= gnome_vfs_address_new_from_string (host
);
140 if (remote
== NULL
) {
141 g_warning ("Unable to resolve address for %s", host
);
145 l_ip
= gnome_vfs_address_get_ipv4 (browser
->priv
->local_address
);
146 r_ip
= gnome_vfs_address_get_ipv4 (remote
);
147 equal
= l_ip
== r_ip
;
149 /* FIXME: Use this when we can depend on gnome-vfs 2.14 */
150 /*equal = gnome_vfs_address_equal (browser->priv->local_address, remote);*/
152 gnome_vfs_address_free (remote
);
158 set_local_address (RBDaapMdnsBrowser
*browser
)
160 char host_name
[256];
161 GnomeVFSResolveHandle
*rh
;
162 GnomeVFSAddress
*address
;
165 if (gethostname (host_name
, sizeof (host_name
)) != 0) {
166 g_warning ("gethostname failed: %s", g_strerror (errno
));
170 res
= gnome_vfs_resolve (host_name
, &rh
);
172 if (res
!= GNOME_VFS_OK
) {
177 while (gnome_vfs_resolve_next_address (rh
, &address
)) {
178 if (browser
->priv
->local_address
== NULL
) {
179 browser
->priv
->local_address
= gnome_vfs_address_dup (address
);
181 gnome_vfs_address_free (address
);
184 gnome_vfs_resolve_free (rh
);
188 resolve_cb (sw_discovery discovery
,
189 sw_discovery_oid oid
,
190 sw_uint32 interface_index
,
191 sw_const_string service_name
,
192 sw_const_string type
,
193 sw_const_string domain
,
194 sw_ipv4_address address
,
196 sw_octets text_record
,
197 sw_ulong text_record_length
,
198 RBDaapMdnsBrowser
*browser
)
202 sw_text_record_iterator it
;
205 host
= g_malloc (16);
208 sw_ipv4_address_name (address
, host
, 16);
210 /* skip local services */
211 if (host_is_local (browser
, host
)) {
215 if (sw_text_record_iterator_init (&it
, text_record
, text_record_length
) == SW_OKAY
) {
216 sw_char key
[SW_TEXT_RECORD_MAX_LEN
];
217 sw_octet val
[SW_TEXT_RECORD_MAX_LEN
];
220 while (sw_text_record_iterator_next (it
, (char *)key
, val
, &val_len
) == SW_OKAY
) {
221 if (strcmp ((char *)key
, "Password") == 0) {
222 if (val_len
>= 4 && strncmp ((char *)val
, "true", 4) == 0) {
226 if (strcmp ((char *)key
, "Machine Name") == 0) {
229 name
= g_strdup ((char *)val
);
233 sw_text_record_iterator_fina (it
);
237 name
= g_strdup (service_name
);
240 g_signal_emit (browser
,
241 signals
[SERVICE_ADDED
],
256 rb_daap_mdns_browser_resolve (RBDaapMdnsBrowser
*browser
,
260 sw_discovery_oid oid
;
262 result
= sw_discovery_resolve (*browser
->priv
->discovery
,
267 (sw_discovery_resolve_reply
) resolve_cb
,
269 (sw_discovery_oid
*) &oid
);
275 browser_add_service (RBDaapMdnsBrowser
*browser
,
276 const char *service_name
)
278 rb_daap_mdns_browser_resolve (browser
, service_name
);
282 browser_remove_service (RBDaapMdnsBrowser
*browser
,
283 const char *service_name
)
285 g_signal_emit (browser
, signals
[SERVICE_REMOVED
], 0, service_name
);
289 browse_cb (sw_discovery discovery
,
290 sw_discovery_oid oid
,
291 sw_discovery_browse_status status
,
292 sw_uint32 interface_index
,
293 sw_const_string name
,
294 sw_const_string type
,
295 sw_const_string domain
,
296 RBDaapMdnsBrowser
*browser
)
298 if (status
== SW_DISCOVERY_BROWSE_ADD_SERVICE
) {
299 browser_add_service (browser
, name
);
300 } else if (status
== SW_DISCOVERY_BROWSE_REMOVE_SERVICE
) {
301 browser_remove_service (browser
, name
);
308 rb_daap_mdns_browser_start (RBDaapMdnsBrowser
*browser
,
313 if (browser
->priv
->discovery
== NULL
) {
315 RB_DAAP_MDNS_BROWSER_ERROR
,
316 RB_DAAP_MDNS_BROWSER_ERROR_NOT_RUNNING
,
318 _("MDNS service is not running"));
322 if (browser
->priv
->oid
!= NULL
) {
324 RB_DAAP_MDNS_BROWSER_ERROR
,
325 RB_DAAP_MDNS_BROWSER_ERROR_FAILED
,
327 _("Browser already active"));
331 browser
->priv
->oid
= g_new0 (sw_discovery_oid
, 1);
333 result
= sw_discovery_browse (*browser
->priv
->discovery
,
337 (sw_discovery_browse_reply
) browse_cb
,
339 (sw_discovery_oid
*) browser
->priv
->oid
);
341 if (result
!= SW_OKAY
) {
342 rb_debug ("Error starting mDNS discovery using Howl");
344 RB_DAAP_MDNS_BROWSER_ERROR
,
345 RB_DAAP_MDNS_BROWSER_ERROR_FAILED
,
347 _("Unable to activate browser"));
356 rb_daap_mdns_browser_stop (RBDaapMdnsBrowser
*browser
,
359 if (browser
->priv
->discovery
== NULL
) {
361 RB_DAAP_MDNS_BROWSER_ERROR
,
362 RB_DAAP_MDNS_BROWSER_ERROR_NOT_RUNNING
,
364 _("MDNS service is not running"));
367 if (browser
->priv
->oid
== NULL
) {
369 RB_DAAP_MDNS_BROWSER_ERROR
,
370 RB_DAAP_MDNS_BROWSER_ERROR_FAILED
,
372 _("Browser is not active"));
377 sw_discovery_cancel (*browser
->priv
->discovery
, *browser
->priv
->oid
);
379 g_free (browser
->priv
->oid
);
380 browser
->priv
->oid
= NULL
;
386 rb_daap_mdns_browser_set_property (GObject
*object
,
393 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
399 rb_daap_mdns_browser_get_property (GObject
*object
,
406 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
412 rb_daap_mdns_browser_class_init (RBDaapMdnsBrowserClass
*klass
)
414 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
416 object_class
->finalize
= rb_daap_mdns_browser_finalize
;
417 object_class
->get_property
= rb_daap_mdns_browser_get_property
;
418 object_class
->set_property
= rb_daap_mdns_browser_set_property
;
420 signals
[SERVICE_ADDED
] =
421 g_signal_new ("service-added",
422 G_TYPE_FROM_CLASS (object_class
),
424 G_STRUCT_OFFSET (RBDaapMdnsBrowserClass
, service_added
),
427 rb_marshal_VOID__STRING_STRING_STRING_UINT_BOOLEAN
,
429 5, G_TYPE_STRING
, G_TYPE_STRING
, G_TYPE_STRING
, G_TYPE_UINT
, G_TYPE_BOOLEAN
);
430 signals
[SERVICE_REMOVED
] =
431 g_signal_new ("service-removed",
432 G_TYPE_FROM_CLASS (object_class
),
434 G_STRUCT_OFFSET (RBDaapMdnsBrowserClass
, service_removed
),
437 g_cclosure_marshal_VOID__STRING
,
441 g_type_class_add_private (klass
, sizeof (RBDaapMdnsBrowserPrivate
));
445 rb_daap_mdns_browser_init (RBDaapMdnsBrowser
*browser
)
447 browser
->priv
= RB_DAAP_MDNS_BROWSER_GET_PRIVATE (browser
);
449 set_local_address (browser
);
451 howl_client_init (browser
);
455 resolver_free (sw_discovery_oid
*oid
,
456 RBDaapMdnsBrowser
*browser
)
458 sw_discovery_cancel (*browser
->priv
->discovery
,
464 rb_daap_mdns_browser_finalize (GObject
*object
)
466 RBDaapMdnsBrowser
*browser
;
468 g_return_if_fail (object
!= NULL
);
469 g_return_if_fail (RB_IS_DAAP_MDNS_BROWSER (object
));
471 browser
= RB_DAAP_MDNS_BROWSER (object
);
473 g_return_if_fail (browser
->priv
!= NULL
);
475 if (browser
->priv
->oid
) {
476 rb_daap_mdns_browser_stop (browser
, NULL
);
479 if (browser
->priv
->resolvers
) {
480 g_slist_foreach (browser
->priv
->resolvers
,
481 (GFunc
)resolver_free
,
483 g_slist_free (browser
->priv
->resolvers
);
486 if (browser
->priv
->discovery
) {
487 sw_discovery_fina (*browser
->priv
->discovery
);
488 g_free (browser
->priv
->discovery
);
491 if (browser
->priv
->watch_id
> 0) {
492 g_source_remove (browser
->priv
->watch_id
);
495 if (browser
->priv
->local_address
) {
496 gnome_vfs_address_free (browser
->priv
->local_address
);
499 G_OBJECT_CLASS (rb_daap_mdns_browser_parent_class
)->finalize (object
);
503 rb_daap_mdns_browser_new (void)
505 if (browser_object
) {
506 g_object_ref (browser_object
);
508 browser_object
= g_object_new (RB_TYPE_DAAP_MDNS_BROWSER
, NULL
);
509 g_object_add_weak_pointer (browser_object
,
510 (gpointer
*) &browser_object
);
513 return RB_DAAP_MDNS_BROWSER (browser_object
);