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