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