]> git.meshlink.io Git - catta/blob - avahi-ui/avahi-ui.c
Fix registration of really large records, like it is necessary for XEP-0174 (jabber...
[catta] / avahi-ui / avahi-ui.c
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 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <string.h>
27 #include <stdarg.h>
28 #include <net/if.h>
29
30 #include <gtk/gtk.h>
31
32 #include <avahi-glib/glib-watch.h>
33 #include <avahi-client/client.h>
34 #include <avahi-client/lookup.h>
35 #include <avahi-common/error.h>
36 #include <avahi-common/address.h>
37 #include <avahi-common/domain.h>
38
39 #include "avahi-ui.h"
40
41 #if defined(HAVE_GDBM) || defined(HAVE_DBM)
42 #include "../avahi-utils/stdb.h"
43 #endif
44
45 /* todo: i18n, HIGify */
46
47 struct _AuiServiceDialog {
48     GtkDialog parent_instance;
49
50     AvahiGLibPoll *glib_poll;
51     AvahiClient *client;
52     AvahiServiceBrowser **browsers;
53     AvahiServiceResolver *resolver;
54     AvahiDomainBrowser *domain_browser;
55     
56     gchar **browse_service_types;
57     gchar *service_type;
58     gchar *domain;
59     gchar *service_name;
60     AvahiProtocol address_family;
61         
62     AvahiAddress address;
63     gchar *host_name;
64     AvahiStringList *txt_data;
65     guint16 port;
66
67     gboolean resolve_service, resolve_service_done;
68     gboolean resolve_host_name, resolve_host_name_done;
69     
70     GtkWidget *domain_label;
71     GtkWidget *domain_button;
72     GtkWidget *service_tree_view;
73     GtkWidget *service_progress_bar;
74     GtkWidget *service_ok_button;
75
76     GtkListStore *service_list_store, *domain_list_store;
77
78     guint service_pulse_timeout;
79     guint domain_pulse_timeout;
80     guint start_idle;
81
82     AvahiIfIndex common_interface;
83     AvahiProtocol common_protocol;
84
85     GtkWidget *domain_dialog;
86     GtkWidget *domain_entry;
87     GtkWidget *domain_tree_view;
88     GtkWidget *domain_progress_bar;
89     GtkWidget *domain_ok_button;
90 };
91
92 enum {
93     PROP_0,
94     PROP_BROWSE_SERVICE_TYPES,
95     PROP_DOMAIN,
96     PROP_SERVICE_TYPE,
97     PROP_SERVICE_NAME,
98     PROP_ADDRESS,
99     PROP_PORT,
100     PROP_HOST_NAME,
101     PROP_TXT_DATA,
102     PROP_RESOLVE_SERVICE,
103     PROP_RESOLVE_HOST_NAME,
104     PROP_ADDRESS_FAMILY
105 };
106
107 enum {
108     SERVICE_COLUMN_IFACE,
109     SERVICE_COLUMN_PROTO,
110     SERVICE_COLUMN_TYPE,
111     SERVICE_COLUMN_NAME,
112     SERVICE_COLUMN_PRETTY_IFACE,
113     SERVICE_COLUMN_PRETTY_TYPE,
114     N_SERVICE_COLUMNS
115 };
116
117 enum {
118     DOMAIN_COLUMN_NAME,
119     DOMAIN_COLUMN_REF,
120     N_DOMAIN_COLUMNS
121 };
122
123 static void aui_service_dialog_finalize(GObject *object);
124 static void aui_service_dialog_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
125 static void aui_service_dialog_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
126
127 G_DEFINE_TYPE(AuiServiceDialog, aui_service_dialog, GTK_TYPE_DIALOG)
128
129 static void aui_service_dialog_class_init(AuiServiceDialogClass *klass) {
130     GObjectClass *object_class;
131     
132     object_class = (GObjectClass*) klass;
133
134     object_class->finalize = aui_service_dialog_finalize;
135     object_class->set_property = aui_service_dialog_set_property;
136     object_class->get_property = aui_service_dialog_get_property;
137
138     g_object_class_install_property(
139             object_class,
140             PROP_BROWSE_SERVICE_TYPES,
141             g_param_spec_pointer("browse_service_types", "Browse Service Types", "A NULL terminated list of service types to browse for",
142                                 G_PARAM_READABLE | G_PARAM_WRITABLE));
143     g_object_class_install_property(
144             object_class,
145             PROP_DOMAIN,
146             g_param_spec_string("domain", "Domain", "The domain to browse in, or NULL for the default domain",
147                                 NULL,
148                                 G_PARAM_READABLE | G_PARAM_WRITABLE));
149     g_object_class_install_property(
150             object_class,
151             PROP_SERVICE_TYPE,
152             g_param_spec_string("service_type", "Service Type", "The service type of the selected service",
153                                 NULL,
154                                 G_PARAM_READABLE | G_PARAM_WRITABLE));
155     g_object_class_install_property(
156             object_class,
157             PROP_SERVICE_NAME,
158             g_param_spec_string("service_name", "Service Name", "The service name of the selected service",
159                                 NULL,
160                                 G_PARAM_READABLE | G_PARAM_WRITABLE));
161     g_object_class_install_property(
162             object_class,
163             PROP_ADDRESS,
164             g_param_spec_pointer("address", "Address", "The address of the resolved service",
165                                 G_PARAM_READABLE));    
166     g_object_class_install_property(
167             object_class,
168             PROP_PORT,
169             g_param_spec_uint("port", "Port", "The IP port number of the resolved service",
170                              0, 0xFFFF, 0,
171                              G_PARAM_READABLE));
172     g_object_class_install_property(
173             object_class,
174             PROP_HOST_NAME,
175             g_param_spec_string("host_name", "Host Name", "The host name of the resolved service",
176                                 NULL,
177                                 G_PARAM_READABLE));
178     g_object_class_install_property(
179             object_class,
180             PROP_TXT_DATA,
181             g_param_spec_pointer("txt_data", "TXT Data", "The TXT data of the resolved service",
182                                 G_PARAM_READABLE));
183     g_object_class_install_property(
184             object_class,
185             PROP_RESOLVE_SERVICE,
186             g_param_spec_boolean("resolve_service", "Resolve service", "Resolve service",
187                                  TRUE,
188                                  G_PARAM_READABLE | G_PARAM_WRITABLE));
189     g_object_class_install_property(
190             object_class,
191             PROP_RESOLVE_HOST_NAME,
192             g_param_spec_boolean("resolve_host_name", "Resolve service host name", "Resolve service host name",
193                                  TRUE,
194                                  G_PARAM_READABLE | G_PARAM_WRITABLE));
195     g_object_class_install_property(
196             object_class,
197             PROP_ADDRESS_FAMILY,
198             g_param_spec_int("address_family", "Address family", "The address family for host name resolution",
199                              AVAHI_PROTO_UNSPEC, AVAHI_PROTO_INET6, AVAHI_PROTO_UNSPEC,
200                              G_PARAM_READABLE | G_PARAM_WRITABLE));
201 }
202
203
204 GtkWidget *aui_service_dialog_new(const gchar *title) {
205     return GTK_WIDGET(g_object_new(
206                               AUI_TYPE_SERVICE_DIALOG,
207                               "has-separator", FALSE,
208                               "title", title,
209                               NULL));
210 }
211
212 static gboolean service_pulse_callback(gpointer data) {
213     AuiServiceDialog *d = AUI_SERVICE_DIALOG(data);
214
215     gtk_progress_bar_pulse(GTK_PROGRESS_BAR(d->service_progress_bar));
216     return TRUE;
217 }
218
219 static gboolean domain_pulse_callback(gpointer data) {
220     AuiServiceDialog *d = AUI_SERVICE_DIALOG(data);
221
222     gtk_progress_bar_pulse(GTK_PROGRESS_BAR(d->domain_progress_bar));
223     return TRUE;
224 }
225
226 static void client_callback(AvahiClient *c, AvahiClientState state, void *userdata) {
227     AuiServiceDialog *d = AUI_SERVICE_DIALOG(userdata);
228
229     if (state == AVAHI_CLIENT_FAILURE) {
230         GtkWidget *m = gtk_message_dialog_new(GTK_WINDOW(d),
231                                               GTK_DIALOG_DESTROY_WITH_PARENT,
232                                               GTK_MESSAGE_ERROR,
233                                               GTK_BUTTONS_CLOSE,
234                                               "Avahi client failure: %s",
235                                               avahi_strerror(avahi_client_errno(c)));
236         gtk_dialog_run(GTK_DIALOG(m));
237         gtk_widget_destroy(m);
238         
239         gtk_dialog_response(GTK_DIALOG(d), GTK_RESPONSE_CANCEL);
240     }
241 }
242
243 static void resolve_callback(
244         AvahiServiceResolver *r,
245         AvahiIfIndex interface,
246         AvahiProtocol protocol,
247         AvahiResolverEvent event,
248         const char *name,
249         const char *type,
250         const char *domain,
251         const char *host_name,
252         const AvahiAddress *a,
253         uint16_t port,
254         AvahiStringList *txt,
255         AvahiLookupResultFlags flags,
256         void *userdata) {
257
258     AuiServiceDialog *d = AUI_SERVICE_DIALOG(userdata);
259
260     switch (event) {
261         case AVAHI_RESOLVER_FOUND:
262
263             d->resolve_service_done = 1;
264
265             g_free(d->service_name);
266             d->service_name = g_strdup(name);
267
268             g_free(d->service_type);
269             d->service_type = g_strdup(type);
270
271             g_free(d->domain);
272             d->domain = g_strdup(domain);
273
274             g_free(d->host_name);
275             d->host_name = g_strdup(host_name);
276
277             d->port = port;
278
279             avahi_string_list_free(d->txt_data);
280             d->txt_data = avahi_string_list_copy(txt);
281             
282             if (a) {
283                 d->resolve_host_name_done = 1;
284                 d->address = *a;
285             }
286
287             gtk_dialog_response(GTK_DIALOG(d), GTK_RESPONSE_OK);
288
289             break;
290
291         case AVAHI_RESOLVER_FAILURE: {
292             GtkWidget *m = gtk_message_dialog_new(GTK_WINDOW(d),
293                                                   GTK_DIALOG_DESTROY_WITH_PARENT,
294                                                   GTK_MESSAGE_ERROR,
295                                                   GTK_BUTTONS_CLOSE,
296                                                   "Avahi resolver failure: %s",
297                                                   avahi_strerror(avahi_client_errno(d->client)));
298             gtk_dialog_run(GTK_DIALOG(m));
299             gtk_widget_destroy(m);
300             
301             gtk_dialog_response(GTK_DIALOG(d), GTK_RESPONSE_CANCEL);
302             break;
303         }
304     }
305 }
306
307
308 static void browse_callback(
309         AvahiServiceBrowser *b,
310         AvahiIfIndex interface,
311         AvahiProtocol protocol,
312         AvahiBrowserEvent event,
313         const char *name,
314         const char *type,
315         const char *domain,
316         AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
317         void* userdata) {
318
319     AuiServiceDialog *d = AUI_SERVICE_DIALOG(userdata);
320     
321     switch (event) {
322
323         case AVAHI_BROWSER_NEW: {
324             gchar *ifs;
325             const gchar *pretty_type;
326             char ifname[IFNAMSIZ];
327             GtkTreeIter iter;
328             GtkTreeSelection *selection;
329
330             if (!(if_indextoname(interface, ifname)))
331                 g_snprintf(ifname, sizeof(ifname), "%i", interface);
332
333             ifs = g_strdup_printf("%s %s", ifname, protocol == AVAHI_PROTO_INET ? "IPv4" : "IPv6");
334
335 #if defined(HAVE_GDBM) || defined(HAVE_DBM)
336             pretty_type = stdb_lookup(type);
337 #else
338             pretty_type = type;
339 #endif            
340             
341             gtk_list_store_append(d->service_list_store, &iter);
342
343             gtk_list_store_set(d->service_list_store, &iter,
344                                SERVICE_COLUMN_IFACE, interface,
345                                SERVICE_COLUMN_PROTO, protocol,
346                                SERVICE_COLUMN_NAME, name,
347                                SERVICE_COLUMN_TYPE, type,
348                                SERVICE_COLUMN_PRETTY_IFACE, ifs,
349                                SERVICE_COLUMN_PRETTY_TYPE, pretty_type,
350                                -1);
351
352             g_free(ifs);
353                     
354             if (d->common_protocol == AVAHI_PROTO_UNSPEC)
355                 d->common_protocol = protocol;
356
357             if (d->common_interface == AVAHI_IF_UNSPEC)
358                 d->common_interface = interface;
359
360             if (d->common_interface != interface || d->common_protocol != protocol) {
361                 gtk_tree_view_column_set_visible(gtk_tree_view_get_column(GTK_TREE_VIEW(d->service_tree_view), 0), TRUE);
362                 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(d->service_tree_view), TRUE);
363             }
364
365             selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(d->service_tree_view));
366             if (!gtk_tree_selection_get_selected(selection, NULL, NULL)) {
367
368                 if (!d->service_type ||
369                     !d->service_name ||
370                     (avahi_domain_equal(d->service_type, type) && strcasecmp(d->service_name, name) == 0)) {
371                     GtkTreePath *path;
372                     
373                     gtk_tree_selection_select_iter(selection, &iter);
374
375                     path = gtk_tree_model_get_path(GTK_TREE_MODEL(d->service_list_store), &iter);
376                     gtk_tree_view_set_cursor(GTK_TREE_VIEW(d->service_tree_view), path, NULL, FALSE);
377                     gtk_tree_path_free(path);
378                 }
379                 
380             }
381             
382             break;
383         }
384             
385         case AVAHI_BROWSER_REMOVE: {
386             GtkTreeIter iter;
387             gboolean valid;
388
389             valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(d->service_list_store), &iter);
390             while (valid) {
391                 gint _interface, _protocol;
392                 gchar *_name, *_type;
393                 gboolean found;
394                 
395                 gtk_tree_model_get(GTK_TREE_MODEL(d->service_list_store), &iter,
396                                    SERVICE_COLUMN_IFACE, &_interface,
397                                    SERVICE_COLUMN_PROTO, &_protocol,
398                                    SERVICE_COLUMN_NAME, &_name,
399                                    SERVICE_COLUMN_TYPE, &_type,
400                                    -1);
401
402                 found = _interface == interface && _protocol == protocol && strcasecmp(_name, name) == 0 && avahi_domain_equal(_type, type);
403                 g_free(_name);
404
405                 if (found) {
406                     gtk_list_store_remove(d->service_list_store, &iter);
407                     break;
408                 }
409                 
410                 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(d->service_list_store), &iter);
411             }
412             
413             break;
414         }
415
416         case AVAHI_BROWSER_FAILURE: {
417             GtkWidget *m = gtk_message_dialog_new(GTK_WINDOW(d),
418                                                   GTK_DIALOG_DESTROY_WITH_PARENT,
419                                                   GTK_MESSAGE_ERROR,
420                                                   GTK_BUTTONS_CLOSE,
421                                                   "Browsing for service type %s in domain %s failed: %s",
422                                                   type, domain,
423                                                   avahi_strerror(avahi_client_errno(d->client)));
424             gtk_dialog_run(GTK_DIALOG(m));
425             gtk_widget_destroy(m);
426
427             /* Fall through */
428         }
429
430         case AVAHI_BROWSER_ALL_FOR_NOW:
431             if (d->service_pulse_timeout > 0) {
432                 g_source_remove(d->service_pulse_timeout);
433                 d->service_pulse_timeout = 0;
434                 gtk_widget_hide(d->service_progress_bar);
435             }
436             break;
437
438         case AVAHI_BROWSER_CACHE_EXHAUSTED:
439             ;
440     }
441 }
442
443 static void domain_make_default_selection(AuiServiceDialog *d, const gchar *name, GtkTreeIter *iter) {
444     GtkTreeSelection *selection;
445     
446     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(d->domain_tree_view));
447     if (!gtk_tree_selection_get_selected(selection, NULL, NULL)) {
448         
449         if (avahi_domain_equal(gtk_entry_get_text(GTK_ENTRY(d->domain_entry)), name)) {
450             GtkTreePath *path;
451             
452             gtk_tree_selection_select_iter(selection, iter);
453             
454             path = gtk_tree_model_get_path(GTK_TREE_MODEL(d->domain_list_store), iter);
455             gtk_tree_view_set_cursor(GTK_TREE_VIEW(d->domain_tree_view), path, NULL, FALSE);
456             gtk_tree_path_free(path);
457         }
458         
459     }
460 }
461
462 static void domain_browse_callback(
463         AvahiDomainBrowser *b,
464         AvahiIfIndex interface,
465         AvahiProtocol protocol,
466         AvahiBrowserEvent event,
467         const char *name,
468         AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
469         void* userdata) {
470
471     AuiServiceDialog *d = AUI_SERVICE_DIALOG(userdata);
472     
473     switch (event) {
474
475         case AVAHI_BROWSER_NEW: {
476             GtkTreeIter iter;
477             gboolean found = FALSE, valid;
478             gint ref;
479
480             valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(d->domain_list_store), &iter);
481             while (valid) {
482                 gchar *_name;
483                 
484                 gtk_tree_model_get(GTK_TREE_MODEL(d->domain_list_store), &iter,
485                                    DOMAIN_COLUMN_NAME, &_name,
486                                    DOMAIN_COLUMN_REF, &ref,
487                                    -1);
488
489                 found = avahi_domain_equal(_name, name);
490                 g_free(_name);
491
492                 if (found)
493                     break;
494                 
495                 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(d->domain_list_store), &iter);
496             }
497
498             if (found) 
499                 gtk_list_store_set(d->domain_list_store, &iter, DOMAIN_COLUMN_REF, ref + 1, -1);
500             else {
501                 gtk_list_store_append(d->domain_list_store, &iter);
502                 
503                 gtk_list_store_set(d->domain_list_store, &iter,
504                                    DOMAIN_COLUMN_NAME, name,
505                                    DOMAIN_COLUMN_REF, 1,
506                                    -1);
507             }
508
509             domain_make_default_selection(d, name, &iter);
510             
511             break;
512         }
513
514         case AVAHI_BROWSER_REMOVE: {
515             gboolean valid;
516             GtkTreeIter iter;
517             
518             valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(d->domain_list_store), &iter);
519             while (valid) {
520                 gint ref;
521                 gchar *_name;
522                 gboolean found;
523                 
524                 gtk_tree_model_get(GTK_TREE_MODEL(d->domain_list_store), &iter,
525                                    DOMAIN_COLUMN_NAME, &_name,
526                                    DOMAIN_COLUMN_REF, &ref,
527                                    -1);
528
529                 found = avahi_domain_equal(_name, name);
530                 g_free(_name);
531
532                 if (found) {
533                     if (ref <= 1)
534                         gtk_list_store_remove(d->service_list_store, &iter);
535                     else
536                         gtk_list_store_set(d->domain_list_store, &iter, DOMAIN_COLUMN_REF, ref - 1, -1);
537                     break;
538                 }
539                 
540                 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(d->domain_list_store), &iter);
541             }
542             
543             break;
544         }
545             
546
547         case AVAHI_BROWSER_FAILURE: {
548             GtkWidget *m = gtk_message_dialog_new(GTK_WINDOW(d),
549                                                   GTK_DIALOG_DESTROY_WITH_PARENT,
550                                                   GTK_MESSAGE_ERROR,
551                                                   GTK_BUTTONS_CLOSE,
552                                                   "Avahi domain browser failure: %s",
553                                                   avahi_strerror(avahi_client_errno(d->client)));
554             gtk_dialog_run(GTK_DIALOG(m));
555             gtk_widget_destroy(m);
556
557             /* Fall through */
558         }
559
560         case AVAHI_BROWSER_ALL_FOR_NOW:
561             if (d->domain_pulse_timeout > 0) {
562                 g_source_remove(d->domain_pulse_timeout);
563                 d->domain_pulse_timeout = 0;
564                 gtk_widget_hide(d->domain_progress_bar);
565             }
566             break;
567
568         case AVAHI_BROWSER_CACHE_EXHAUSTED:
569             ;
570     }
571 }
572
573 static const gchar *get_domain_name(AuiServiceDialog *d) {
574     const gchar *domain;
575     
576     g_return_val_if_fail(d, NULL);
577     
578     if (d->domain)
579         return d->domain;
580
581     if (!(domain = avahi_client_get_domain_name(d->client))) {
582         GtkWidget *m = gtk_message_dialog_new(GTK_WINDOW(d),
583                                               GTK_DIALOG_DESTROY_WITH_PARENT,
584                                               GTK_MESSAGE_ERROR,
585                                               GTK_BUTTONS_CLOSE,
586                                               "Failed to read Avahi domain : %s",
587                                               avahi_strerror(avahi_client_errno(d->client)));
588         gtk_dialog_run(GTK_DIALOG(m));
589         gtk_widget_destroy(m);
590
591         return NULL;
592     }
593
594     return domain;
595 }
596
597 static gboolean start_callback(gpointer data) {
598     int error;
599     AuiServiceDialog *d = AUI_SERVICE_DIALOG(data);
600     gchar **st;
601     AvahiServiceBrowser **sb;
602     unsigned i;
603     const char *domain;
604
605     d->start_idle = 0;
606     
607     if (!d->browse_service_types || !*d->browse_service_types) {
608         g_warning("Browse service type list is empty!");
609         return FALSE;
610     }
611
612     if (!d->client) {
613         if (!(d->client = avahi_client_new(avahi_glib_poll_get(d->glib_poll), 0, client_callback, d, &error))) {
614             
615             GtkWidget *m = gtk_message_dialog_new(GTK_WINDOW(d),
616                                                   GTK_DIALOG_DESTROY_WITH_PARENT,
617                                                   GTK_MESSAGE_ERROR,
618                                                   GTK_BUTTONS_CLOSE,
619                                                   "Failed to connect to Avahi server: %s",
620                                                   avahi_strerror(error));
621             gtk_dialog_run(GTK_DIALOG(m));
622             gtk_widget_destroy(m);
623             
624             gtk_dialog_response(GTK_DIALOG(d), GTK_RESPONSE_CANCEL);
625             return FALSE;
626         }
627     }
628
629     if (!(domain = get_domain_name(d))) {
630         gtk_dialog_response(GTK_DIALOG(d), GTK_RESPONSE_CANCEL);
631         return FALSE;
632     }
633
634     g_assert(domain);
635
636     if (avahi_domain_equal(domain, "local."))
637         gtk_label_set_markup(GTK_LABEL(d->domain_label), "Browsing for services on <b>local network</b>:");
638     else {
639         gchar *t = g_strdup_printf("Browsing for services in domain <b>%s</b>:", domain);
640         gtk_label_set_markup(GTK_LABEL(d->domain_label), t);
641         g_free(t);
642     }
643     
644     if (d->browsers) {
645         for (sb = d->browsers; *sb; sb++)
646             avahi_service_browser_free(*sb);
647
648         g_free(d->browsers);
649         d->browsers = NULL;
650     }
651
652     gtk_list_store_clear(GTK_LIST_STORE(d->service_list_store));
653     d->common_interface = AVAHI_IF_UNSPEC;
654     d->common_protocol = AVAHI_PROTO_UNSPEC;
655
656     gtk_tree_view_column_set_visible(gtk_tree_view_get_column(GTK_TREE_VIEW(d->service_tree_view), 0), FALSE);
657     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(d->service_tree_view), gtk_tree_view_column_get_visible(gtk_tree_view_get_column(GTK_TREE_VIEW(d->service_tree_view), 2)));
658     gtk_widget_show(d->service_progress_bar);
659
660     if (d->service_pulse_timeout <= 0)
661         d->service_pulse_timeout = g_timeout_add(100, service_pulse_callback, d);
662
663     for (i = 0; d->browse_service_types[i]; i++)
664         ;
665     g_assert(i > 0);
666
667     d->browsers = g_new0(AvahiServiceBrowser*, i+1);
668     for (st = d->browse_service_types, sb = d->browsers; *st; st++, sb++) {
669
670         if (!(*sb = avahi_service_browser_new(d->client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, *st, d->domain, 0, browse_callback, d))) {
671             GtkWidget *m = gtk_message_dialog_new(GTK_WINDOW(d),
672                                                   GTK_DIALOG_DESTROY_WITH_PARENT,
673                                                   GTK_MESSAGE_ERROR,
674                                                   GTK_BUTTONS_CLOSE,
675                                                   "Failed to create browser for %s: %s",
676                                                   *st,
677                                                   avahi_strerror(avahi_client_errno(d->client)));
678             gtk_dialog_run(GTK_DIALOG(m));
679             gtk_widget_destroy(m);
680             
681             gtk_dialog_response(GTK_DIALOG(d), GTK_RESPONSE_CANCEL);
682             return FALSE;
683
684         }
685     }
686
687     return FALSE;
688 }
689
690 static void aui_service_dialog_finalize(GObject *object) {
691     AuiServiceDialog *d = AUI_SERVICE_DIALOG(object);
692
693     if (d->domain_pulse_timeout > 0)
694         g_source_remove(d->domain_pulse_timeout);
695
696     if (d->service_pulse_timeout > 0)
697         g_source_remove(d->service_pulse_timeout);
698
699     if (d->start_idle > 0)
700         g_source_remove(d->start_idle);
701     
702     g_free(d->host_name);
703     g_free(d->domain);
704     g_free(d->service_name);
705
706     avahi_string_list_free(d->txt_data);
707     
708     g_strfreev(d->browse_service_types);
709
710     if (d->domain_browser)
711         avahi_domain_browser_free(d->domain_browser);
712     
713     if (d->resolver)
714         avahi_service_resolver_free(d->resolver);
715     
716     if (d->browsers) {
717         AvahiServiceBrowser **sb;
718
719         for (sb = d->browsers; *sb; sb++)
720             avahi_service_browser_free(*sb);
721
722         g_free(d->browsers);
723     }
724
725     if (d->client)
726         avahi_client_free(d->client);
727
728     if (d->glib_poll)
729         avahi_glib_poll_free(d->glib_poll);
730
731     G_OBJECT_CLASS(aui_service_dialog_parent_class)->finalize(object);
732 }
733
734 static void service_row_activated_callback(GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data) {
735     AuiServiceDialog *d = AUI_SERVICE_DIALOG(user_data);
736
737     gtk_dialog_response(GTK_DIALOG(d), GTK_RESPONSE_OK);
738 }
739
740 static void service_selection_changed_callback(GtkTreeSelection *selection, gpointer user_data) {
741     AuiServiceDialog *d = AUI_SERVICE_DIALOG(user_data);
742
743     gtk_widget_set_sensitive(d->service_ok_button, gtk_tree_selection_get_selected(selection, NULL, NULL));
744 }
745
746 static void response_callback(GtkDialog *dialog, gint response, gpointer user_data) {
747     AuiServiceDialog *d = AUI_SERVICE_DIALOG(user_data);
748
749     if (response == GTK_RESPONSE_OK &&
750         ((d->resolve_service && !d->resolve_service_done) ||
751          (d->resolve_host_name && !d->resolve_host_name_done))) {
752         
753         GtkTreeIter iter;
754         gint interface, protocol;
755         gchar *name, *type;
756         GdkCursor *cursor;
757
758         g_signal_stop_emission(dialog, g_signal_lookup("response", gtk_dialog_get_type()), 0);
759
760         if (d->resolver)
761             return;
762
763         g_return_if_fail(gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(d->service_tree_view)), NULL, &iter));
764
765         gtk_tree_model_get(GTK_TREE_MODEL(d->service_list_store), &iter,
766                            SERVICE_COLUMN_IFACE, &interface,
767                            SERVICE_COLUMN_PROTO, &protocol,
768                            SERVICE_COLUMN_NAME, &name,
769                            SERVICE_COLUMN_TYPE, &type, -1);
770
771         g_return_if_fail(d->client);
772
773         gtk_widget_set_sensitive(GTK_WIDGET(dialog), FALSE);
774         cursor = gdk_cursor_new(GDK_WATCH);
775         gdk_window_set_cursor(GTK_WIDGET(dialog)->window, cursor);
776         gdk_cursor_unref(cursor);
777
778         if (!(d->resolver = avahi_service_resolver_new(
779                       d->client, interface, protocol, name, type, d->domain,
780                       d->address_family, !d->resolve_host_name ? AVAHI_LOOKUP_NO_ADDRESS : 0, resolve_callback, d))) {
781
782             GtkWidget *m = gtk_message_dialog_new(GTK_WINDOW(d),
783                                                   GTK_DIALOG_DESTROY_WITH_PARENT,
784                                                   GTK_MESSAGE_ERROR,
785                                                   GTK_BUTTONS_CLOSE,
786                                                   "Failed to create resolver for %s of type %s in domain %s: %s",
787                                                   name, type, d->domain,
788                                                   avahi_strerror(avahi_client_errno(d->client)));
789             gtk_dialog_run(GTK_DIALOG(m));
790             gtk_widget_destroy(m);
791             
792             gtk_dialog_response(GTK_DIALOG(d), GTK_RESPONSE_CANCEL);
793             return;
794         }
795     }
796 }
797
798 static gboolean is_valid_domain_suffix(const gchar *n) {
799     gchar label[AVAHI_LABEL_MAX];
800     
801     if (!avahi_is_valid_domain_name(n))
802         return FALSE;
803
804     if (!avahi_unescape_label(&n, label, sizeof(label)))
805         return FALSE;
806
807     /* At least one label */
808     
809     return !!label[0];
810 }
811
812 static void domain_row_activated_callback(GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data) {
813     AuiServiceDialog *d = AUI_SERVICE_DIALOG(user_data);
814
815     if (is_valid_domain_suffix(gtk_entry_get_text(GTK_ENTRY(d->domain_entry))))
816         gtk_dialog_response(GTK_DIALOG(d->domain_dialog), GTK_RESPONSE_OK);
817 }
818
819 static void domain_selection_changed_callback(GtkTreeSelection *selection, gpointer user_data) {
820     GtkTreeIter iter;
821     AuiServiceDialog *d = AUI_SERVICE_DIALOG(user_data);
822     gchar *name;
823
824     g_return_if_fail(gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(d->domain_tree_view)), NULL, &iter));
825
826     gtk_tree_model_get(GTK_TREE_MODEL(d->domain_list_store), &iter,
827                        DOMAIN_COLUMN_NAME, &name, -1);
828
829     gtk_entry_set_text(GTK_ENTRY(d->domain_entry), name);
830 }
831
832 static void domain_entry_changed_callback(GtkEditable *editable, gpointer user_data) {
833     AuiServiceDialog *d = AUI_SERVICE_DIALOG(user_data);
834
835     gtk_widget_set_sensitive(d->domain_ok_button, is_valid_domain_suffix(gtk_entry_get_text(GTK_ENTRY(d->domain_entry))));
836 }
837
838 static void domain_button_clicked(GtkButton *button,  gpointer user_data) {
839     GtkWidget *vbox, *vbox2, *scrolled_window;
840     GtkTreeSelection *selection;
841     GtkCellRenderer *renderer;
842     GtkTreeViewColumn *column;
843     AuiServiceDialog *d = AUI_SERVICE_DIALOG(user_data);
844     const gchar *domain;
845     GtkTreeIter iter;
846
847     g_return_if_fail(!d->domain_dialog);
848     g_return_if_fail(!d->domain_browser);
849
850     if (!(domain = get_domain_name(d))) {
851         gtk_dialog_response(GTK_DIALOG(d), GTK_RESPONSE_CANCEL);
852         return;
853     }
854
855     if (!(d->domain_browser = avahi_domain_browser_new(d->client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, NULL, AVAHI_DOMAIN_BROWSER_BROWSE, 0, domain_browse_callback, d))) {
856         GtkWidget *m = gtk_message_dialog_new(GTK_WINDOW(d),
857                                               GTK_DIALOG_DESTROY_WITH_PARENT,
858                                               GTK_MESSAGE_ERROR,
859                                               GTK_BUTTONS_CLOSE,
860                                               "Failed to create domain browser: %s",
861                                               avahi_strerror(avahi_client_errno(d->client)));
862         gtk_dialog_run(GTK_DIALOG(m));
863         gtk_widget_destroy(m);
864         
865         gtk_dialog_response(GTK_DIALOG(d), GTK_RESPONSE_CANCEL);
866         return;
867     }
868     
869     d->domain_dialog = gtk_dialog_new();
870     gtk_container_set_border_width(GTK_CONTAINER(d->domain_dialog), 5);
871     gtk_window_set_title(GTK_WINDOW(d->domain_dialog), "Change domain");
872     gtk_dialog_set_has_separator(GTK_DIALOG(d->domain_dialog), FALSE);
873     
874     vbox = gtk_vbox_new(FALSE, 8);
875     gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
876     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(d->domain_dialog)->vbox), vbox, TRUE, TRUE, 0);
877
878     d->domain_entry = gtk_entry_new_with_max_length(AVAHI_DOMAIN_NAME_MAX);
879     gtk_entry_set_text(GTK_ENTRY(d->domain_entry), domain);
880     gtk_entry_set_activates_default(GTK_ENTRY(d->domain_entry), TRUE);
881     g_signal_connect(d->domain_entry, "changed", G_CALLBACK(domain_entry_changed_callback), d);
882     gtk_box_pack_start(GTK_BOX(vbox), d->domain_entry, FALSE, FALSE, 0);
883     
884     vbox2 = gtk_vbox_new(FALSE, 8);
885     gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
886
887     scrolled_window = gtk_scrolled_window_new(NULL, NULL);
888     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
889     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW (scrolled_window), GTK_SHADOW_ETCHED_IN);
890     gtk_box_pack_start(GTK_BOX(vbox2), scrolled_window, TRUE, TRUE, 0);
891
892     d->domain_list_store = gtk_list_store_new(N_DOMAIN_COLUMNS, G_TYPE_STRING, G_TYPE_INT);
893
894     d->domain_tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(d->domain_list_store));
895     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(d->domain_tree_view), FALSE);
896     g_signal_connect(d->domain_tree_view, "row-activated", G_CALLBACK(domain_row_activated_callback), d);
897     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(d->domain_tree_view));
898     gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
899     g_signal_connect(selection, "changed", G_CALLBACK(domain_selection_changed_callback), d);
900
901     renderer = gtk_cell_renderer_text_new();
902     column = gtk_tree_view_column_new_with_attributes("Name", renderer, "text", DOMAIN_COLUMN_NAME, NULL);
903     gtk_tree_view_column_set_expand(column, TRUE);
904     gtk_tree_view_append_column(GTK_TREE_VIEW(d->domain_tree_view), column);
905
906     gtk_tree_view_set_search_column(GTK_TREE_VIEW(d->domain_tree_view), DOMAIN_COLUMN_NAME);
907     gtk_container_add(GTK_CONTAINER(scrolled_window), d->domain_tree_view);
908
909     d->domain_progress_bar = gtk_progress_bar_new();
910     gtk_progress_bar_set_text(GTK_PROGRESS_BAR(d->domain_progress_bar), "Browsing ...");
911     gtk_progress_bar_set_pulse_step(GTK_PROGRESS_BAR(d->domain_progress_bar), 0.1);
912     gtk_box_pack_end(GTK_BOX(vbox2), d->domain_progress_bar, FALSE, FALSE, 0);
913     
914     gtk_dialog_add_button(GTK_DIALOG(d->domain_dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
915     d->domain_ok_button = GTK_WIDGET(gtk_dialog_add_button(GTK_DIALOG(d->domain_dialog), GTK_STOCK_OK, GTK_RESPONSE_OK));
916     gtk_dialog_set_default_response(GTK_DIALOG(d->domain_dialog), GTK_RESPONSE_OK);
917     gtk_widget_set_sensitive(d->domain_ok_button, is_valid_domain_suffix(gtk_entry_get_text(GTK_ENTRY(d->domain_entry))));
918
919     gtk_widget_grab_default(d->domain_ok_button);
920     gtk_widget_grab_focus(d->domain_entry);
921
922     gtk_window_set_default_size(GTK_WINDOW(d->domain_dialog), 300, 300);
923     
924     gtk_widget_show_all(vbox);
925
926     gtk_list_store_append(d->domain_list_store, &iter);
927     gtk_list_store_set(d->domain_list_store, &iter, DOMAIN_COLUMN_NAME, "local", DOMAIN_COLUMN_REF, 1, -1);
928     domain_make_default_selection(d, "local", &iter);
929
930     d->domain_pulse_timeout = g_timeout_add(100, domain_pulse_callback, d);
931     
932     if (gtk_dialog_run(GTK_DIALOG(d->domain_dialog)) == GTK_RESPONSE_OK)
933         aui_service_dialog_set_domain(d, gtk_entry_get_text(GTK_ENTRY(d->domain_entry)));
934
935     gtk_widget_destroy(d->domain_dialog);
936     d->domain_dialog = NULL;
937
938     if (d->domain_pulse_timeout > 0) {
939         g_source_remove(d->domain_pulse_timeout);
940         d->domain_pulse_timeout = 0;
941     }
942
943     avahi_domain_browser_free(d->domain_browser);
944     d->domain_browser = NULL;
945
946 }
947
948 static void aui_service_dialog_init(AuiServiceDialog *d) {
949     GtkWidget *vbox, *vbox2, *hbox, *scrolled_window;
950     GtkCellRenderer *renderer;
951     GtkTreeViewColumn *column;
952     GtkTreeSelection *selection;
953
954     d->host_name = NULL;
955     d->domain = NULL;
956     d->service_name = NULL;
957     d->txt_data = NULL;
958     d->browse_service_types = NULL;
959     memset(&d->address, 0, sizeof(d->address));
960     d->port = 0;
961     d->resolve_host_name = d->resolve_service = TRUE;
962     d->resolve_host_name_done = d->resolve_service_done = FALSE;
963     d->address_family = AVAHI_PROTO_UNSPEC;
964
965     d->glib_poll = NULL;
966     d->client = NULL;
967     d->browsers = NULL;
968     d->resolver = NULL;
969     d->domain_browser = NULL;
970
971     d->service_pulse_timeout = 0;
972     d->domain_pulse_timeout = 0;
973     d->start_idle = 0;
974     d->common_interface = AVAHI_IF_UNSPEC;
975     d->common_protocol = AVAHI_PROTO_UNSPEC;
976
977     d->domain_dialog = NULL;
978     d->domain_entry = NULL;
979     d->domain_tree_view = NULL;
980     d->domain_progress_bar = NULL;
981     d->domain_ok_button = NULL;
982
983     d->service_list_store = d->domain_list_store = NULL;
984     
985     gtk_widget_push_composite_child();
986
987     gtk_container_set_border_width(GTK_CONTAINER(d), 5);
988
989     vbox = gtk_vbox_new(FALSE, 8);
990     gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
991     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(d)->vbox), vbox, TRUE, TRUE, 0);
992
993     hbox = gtk_hbox_new(FALSE, 8);
994     gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
995     
996     d->domain_label = gtk_label_new("Initializing...");
997     gtk_label_set_ellipsize(GTK_LABEL(d->domain_label), TRUE);
998     gtk_misc_set_alignment(GTK_MISC(d->domain_label), 0, 0.5);
999     gtk_box_pack_start(GTK_BOX(hbox), d->domain_label, TRUE, TRUE, 0);
1000     
1001     d->domain_button = gtk_button_new_with_mnemonic("Change _domain...");
1002     g_signal_connect(d->domain_button, "clicked", G_CALLBACK(domain_button_clicked), d);
1003     gtk_box_pack_end(GTK_BOX(hbox), d->domain_button, FALSE, TRUE, 0);
1004
1005     vbox2 = gtk_vbox_new(FALSE, 8);
1006     gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
1007
1008     scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1009     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1010     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW (scrolled_window), GTK_SHADOW_ETCHED_IN);
1011     gtk_box_pack_start(GTK_BOX(vbox2), scrolled_window, TRUE, TRUE, 0);
1012
1013     d->service_list_store = gtk_list_store_new(N_SERVICE_COLUMNS, G_TYPE_INT, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
1014
1015     d->service_tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(d->service_list_store));
1016     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(d->service_tree_view), FALSE);
1017     g_signal_connect(d->service_tree_view, "row-activated", G_CALLBACK(service_row_activated_callback), d);
1018     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(d->service_tree_view));
1019     gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
1020     g_signal_connect(selection, "changed", G_CALLBACK(service_selection_changed_callback), d);
1021     
1022     renderer = gtk_cell_renderer_text_new();
1023     column = gtk_tree_view_column_new_with_attributes("Location", renderer, "text", SERVICE_COLUMN_PRETTY_IFACE, NULL);
1024     gtk_tree_view_column_set_visible(column, FALSE);
1025     gtk_tree_view_append_column(GTK_TREE_VIEW(d->service_tree_view), column);
1026     
1027     renderer = gtk_cell_renderer_text_new();
1028     column = gtk_tree_view_column_new_with_attributes("Name", renderer, "text", SERVICE_COLUMN_NAME, NULL);
1029     gtk_tree_view_column_set_expand(column, TRUE);
1030     gtk_tree_view_append_column(GTK_TREE_VIEW(d->service_tree_view), column);
1031
1032     renderer = gtk_cell_renderer_text_new();
1033     column = gtk_tree_view_column_new_with_attributes("Type", renderer, "text", SERVICE_COLUMN_PRETTY_TYPE, NULL);
1034     gtk_tree_view_column_set_visible(column, FALSE);
1035     gtk_tree_view_append_column(GTK_TREE_VIEW(d->service_tree_view), column);
1036
1037     gtk_tree_view_set_search_column(GTK_TREE_VIEW(d->service_tree_view), SERVICE_COLUMN_NAME);
1038     gtk_container_add(GTK_CONTAINER(scrolled_window), d->service_tree_view);
1039
1040     d->service_progress_bar = gtk_progress_bar_new();
1041     gtk_progress_bar_set_text(GTK_PROGRESS_BAR(d->service_progress_bar), "Browsing ...");
1042     gtk_progress_bar_set_pulse_step(GTK_PROGRESS_BAR(d->service_progress_bar), 0.1);
1043     gtk_box_pack_end(GTK_BOX(vbox2), d->service_progress_bar, FALSE, FALSE, 0);
1044     
1045     gtk_dialog_add_button(GTK_DIALOG(d), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
1046     d->service_ok_button = GTK_WIDGET(gtk_dialog_add_button(GTK_DIALOG(d), GTK_STOCK_OK, GTK_RESPONSE_OK));
1047     gtk_dialog_set_default_response(GTK_DIALOG(d), GTK_RESPONSE_OK);
1048     gtk_widget_set_sensitive(d->service_ok_button, FALSE);
1049
1050     gtk_widget_grab_default(d->service_ok_button);
1051     gtk_widget_grab_focus(d->service_tree_view);
1052
1053     gtk_window_set_default_size(GTK_WINDOW(d), 400, 300);
1054     
1055     gtk_widget_show_all(vbox);
1056
1057     gtk_widget_pop_composite_child();
1058
1059     d->glib_poll = avahi_glib_poll_new(NULL, G_PRIORITY_DEFAULT);
1060
1061     d->service_pulse_timeout = g_timeout_add(100, service_pulse_callback, d);
1062     d->start_idle = g_idle_add(start_callback, d);
1063
1064     g_signal_connect(d, "response", G_CALLBACK(response_callback), d);
1065 }
1066
1067 static void restart_browsing(AuiServiceDialog *d) {
1068     g_return_if_fail(AUI_IS_SERVICE_DIALOG(d));
1069
1070     if (d->start_idle <= 0)
1071         d->start_idle = g_idle_add(start_callback, d);
1072 }
1073
1074 void aui_service_dialog_set_browse_service_types(AuiServiceDialog *d, const char *type, ...) {
1075     va_list ap, apcopy;
1076     const char *t;
1077     unsigned u;
1078     
1079     g_return_if_fail(AUI_IS_SERVICE_DIALOG(d));
1080     g_return_if_fail(type);
1081
1082     g_strfreev(d->browse_service_types);
1083     
1084     va_copy(apcopy, ap);
1085     
1086     va_start(ap, type);
1087     for (u = 1; va_arg(ap, const char *); u++)
1088         ;
1089     va_end(ap);
1090
1091     d->browse_service_types = g_new0(gchar*, u+1);
1092     d->browse_service_types[0] = g_strdup(type);
1093     
1094     va_start(apcopy, type);
1095     for (u = 1; (t = va_arg(apcopy, const char*)); u++)
1096         d->browse_service_types[u] = g_strdup(t);
1097     va_end(apcopy);
1098
1099     if (d->browse_service_types[0] && d->browse_service_types[1]) {
1100         /* Multiple service types, enable headers */
1101     
1102         gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(d->service_tree_view), TRUE);
1103         gtk_tree_view_column_set_visible(gtk_tree_view_get_column(GTK_TREE_VIEW(d->service_tree_view), 2), TRUE);
1104     }
1105
1106     restart_browsing(d);
1107 }
1108
1109 void aui_service_dialog_set_browse_service_typesv(AuiServiceDialog *d, const char *const*types) {
1110     
1111     g_return_if_fail(AUI_IS_SERVICE_DIALOG(d));
1112     g_return_if_fail(types);
1113     g_return_if_fail(*types);
1114
1115     g_strfreev(d->browse_service_types);
1116     d->browse_service_types = g_strdupv((char**) types);
1117
1118     if (d->browse_service_types[0] && d->browse_service_types[1]) {
1119         /* Multiple service types, enable headers */
1120     
1121         gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(d->service_tree_view), TRUE);
1122         gtk_tree_view_column_set_visible(gtk_tree_view_get_column(GTK_TREE_VIEW(d->service_tree_view), 2), TRUE);
1123     }
1124
1125     restart_browsing(d);
1126 }
1127
1128 const gchar*const* aui_service_dialog_get_browse_service_types(AuiServiceDialog *d) {
1129     g_return_val_if_fail(AUI_IS_SERVICE_DIALOG(d), NULL);
1130
1131     return (const char* const*) d->browse_service_types;
1132 }
1133
1134 void aui_service_dialog_set_domain(AuiServiceDialog *d, const char *domain) {
1135     g_return_if_fail(AUI_IS_SERVICE_DIALOG(d));
1136     g_return_if_fail(!domain || is_valid_domain_suffix(domain));
1137
1138     g_free(d->domain);
1139     d->domain = domain ? avahi_normalize_name_strdup(domain) : NULL;
1140     
1141     restart_browsing(d);
1142 }
1143
1144 const char* aui_service_dialog_get_domain(AuiServiceDialog *d) {
1145     g_return_val_if_fail(AUI_IS_SERVICE_DIALOG(d), NULL);
1146     
1147     return d->domain;
1148 }
1149
1150 void aui_service_dialog_set_service_name(AuiServiceDialog *d, const char *name) {
1151     g_return_if_fail(AUI_IS_SERVICE_DIALOG(d));
1152
1153     g_free(d->service_name);
1154     d->service_name = g_strdup(name);
1155 }
1156
1157 const char* aui_service_dialog_get_service_name(AuiServiceDialog *d) {
1158     g_return_val_if_fail(AUI_IS_SERVICE_DIALOG(d), NULL);
1159
1160     return d->service_name;
1161 }
1162
1163 void aui_service_dialog_set_service_type(AuiServiceDialog *d, const char*stype) {
1164     g_return_if_fail(AUI_IS_SERVICE_DIALOG(d));
1165
1166     g_free(d->service_type);
1167     d->service_type = g_strdup(stype);
1168 }
1169
1170 const char* aui_service_dialog_get_service_type(AuiServiceDialog *d) {
1171     g_return_val_if_fail(AUI_IS_SERVICE_DIALOG(d), NULL);
1172
1173     return d->service_type;
1174 }
1175
1176 const AvahiAddress* aui_service_dialog_get_address(AuiServiceDialog *d) {
1177     g_return_val_if_fail(AUI_IS_SERVICE_DIALOG(d), NULL);
1178     g_return_val_if_fail(d->resolve_service_done && d->resolve_host_name_done, NULL);
1179
1180     return &d->address;
1181 }
1182
1183 guint16 aui_service_dialog_get_port(AuiServiceDialog *d) {
1184     g_return_val_if_fail(AUI_IS_SERVICE_DIALOG(d), 0);
1185     g_return_val_if_fail(d->resolve_service_done, 0);
1186
1187     return d->port;
1188 }
1189
1190 const char* aui_service_dialog_get_host_name(AuiServiceDialog *d) {
1191     g_return_val_if_fail(AUI_IS_SERVICE_DIALOG(d), NULL);
1192     g_return_val_if_fail(d->resolve_service_done, NULL);
1193
1194     return d->host_name;
1195 }
1196
1197 const AvahiStringList *aui_service_dialog_get_txt_data(AuiServiceDialog *d) {
1198     g_return_val_if_fail(AUI_IS_SERVICE_DIALOG(d), NULL);
1199     g_return_val_if_fail(d->resolve_service_done, NULL);
1200
1201     return d->txt_data;
1202 }
1203
1204 void aui_service_dialog_set_resolve_service(AuiServiceDialog *d, gboolean resolve) {
1205     g_return_if_fail(AUI_IS_SERVICE_DIALOG(d));
1206     
1207     d->resolve_service = resolve;
1208 }
1209
1210 gboolean aui_service_dialog_get_resolve_service(AuiServiceDialog *d) {
1211     g_return_val_if_fail(AUI_IS_SERVICE_DIALOG(d), FALSE);
1212
1213     return d->resolve_service;
1214 }
1215
1216 void aui_service_dialog_set_resolve_host_name(AuiServiceDialog *d, gboolean resolve) {
1217     g_return_if_fail(AUI_IS_SERVICE_DIALOG(d));
1218     
1219     d->resolve_host_name = resolve;
1220 }
1221
1222 gboolean aui_service_dialog_get_resolve_host_name(AuiServiceDialog *d) {
1223     g_return_val_if_fail(AUI_IS_SERVICE_DIALOG(d), FALSE);
1224
1225     return d->resolve_host_name;
1226 }
1227
1228 void aui_service_dialog_set_address_family(AuiServiceDialog *d, AvahiProtocol proto) {
1229     g_return_if_fail(AUI_IS_SERVICE_DIALOG(d));
1230     g_return_if_fail(proto == AVAHI_PROTO_UNSPEC || proto == AVAHI_PROTO_INET || proto == AVAHI_PROTO_INET6);
1231
1232     d->address_family = proto;
1233 }
1234
1235 AvahiProtocol aui_service_dialog_get_address_family(AuiServiceDialog *d) {
1236     g_return_val_if_fail(AUI_IS_SERVICE_DIALOG(d), AVAHI_PROTO_UNSPEC);
1237
1238     return d->address_family;
1239 }
1240
1241 static void aui_service_dialog_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) {
1242     AuiServiceDialog *d = AUI_SERVICE_DIALOG(object);
1243     
1244     switch (prop_id) {
1245         case PROP_BROWSE_SERVICE_TYPES:
1246             aui_service_dialog_set_browse_service_typesv(d, g_value_get_pointer(value));
1247             break;
1248
1249         case PROP_DOMAIN:
1250             aui_service_dialog_set_domain(d, g_value_get_string(value));
1251             break;
1252
1253         case PROP_SERVICE_TYPE:
1254             aui_service_dialog_set_service_type(d, g_value_get_string(value));
1255             break;
1256
1257         case PROP_SERVICE_NAME:
1258             aui_service_dialog_set_service_name(d, g_value_get_string(value));
1259             break;
1260             
1261         case PROP_RESOLVE_SERVICE:
1262             aui_service_dialog_set_resolve_service(d, g_value_get_boolean(value));
1263             break;
1264
1265         case PROP_RESOLVE_HOST_NAME:
1266             aui_service_dialog_set_resolve_host_name(d, g_value_get_boolean(value));
1267             break;
1268
1269         case PROP_ADDRESS_FAMILY:
1270             aui_service_dialog_set_address_family(d, g_value_get_int(value));
1271             break;
1272             
1273         default:
1274             G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
1275             break;
1276     }
1277 }
1278
1279 static void aui_service_dialog_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) {
1280     AuiServiceDialog *d = AUI_SERVICE_DIALOG(object);
1281     
1282     switch (prop_id) {
1283         case PROP_BROWSE_SERVICE_TYPES:
1284             g_value_set_pointer(value, (gpointer) aui_service_dialog_get_browse_service_types(d));
1285             break;
1286
1287         case PROP_DOMAIN:
1288             g_value_set_string(value, aui_service_dialog_get_domain(d));
1289             break;
1290
1291         case PROP_SERVICE_TYPE:
1292             g_value_set_string(value, aui_service_dialog_get_service_type(d));
1293             break;
1294
1295         case PROP_SERVICE_NAME:
1296             g_value_set_string(value, aui_service_dialog_get_service_name(d));
1297             break;
1298
1299         case PROP_ADDRESS:
1300             g_value_set_pointer(value, (gpointer) aui_service_dialog_get_address(d));
1301             break;
1302
1303         case PROP_PORT:
1304             g_value_set_uint(value, aui_service_dialog_get_port(d));
1305             break;
1306
1307         case PROP_HOST_NAME:
1308             g_value_set_string(value, aui_service_dialog_get_host_name(d));
1309             break;
1310                                
1311         case PROP_TXT_DATA:
1312             g_value_set_pointer(value, (gpointer) aui_service_dialog_get_txt_data(d));
1313             break;
1314             
1315         case PROP_RESOLVE_SERVICE:
1316             g_value_set_boolean(value, aui_service_dialog_get_resolve_service(d));
1317             break;
1318
1319         case PROP_RESOLVE_HOST_NAME:
1320             g_value_set_boolean(value, aui_service_dialog_get_resolve_host_name(d));
1321             break;
1322
1323         case PROP_ADDRESS_FAMILY:
1324             g_value_set_int(value, aui_service_dialog_get_address_family(d));
1325             break;
1326             
1327         default:
1328             G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
1329             break;
1330     }
1331 }
1332