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
get_default_response(GtkDialog * dlg)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
G_DEFINE_TYPE(AuiServiceDialog,aui_service_dialog,GTK_TYPE_DIALOG)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
aui_service_dialog_new_valist(const gchar * title,GtkWindow * parent,const gchar * first_button_text,va_list varargs)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
aui_service_dialog_new(const gchar * title,GtkWindow * parent,const gchar * first_button_text,...)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
service_pulse_callback(gpointer data)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
domain_pulse_callback(gpointer data)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
client_callback(AvahiClient * c,AvahiClientState state,void * userdata)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
resolve_callback(AvahiServiceResolver * r G_GNUC_UNUSED,AvahiIfIndex interface G_GNUC_UNUSED,AvahiProtocol protocol G_GNUC_UNUSED,AvahiResolverEvent event,const char * name,const char * type,const char * domain,const char * host_name,const AvahiAddress * a,uint16_t port,AvahiStringList * txt,AvahiLookupResultFlags flags G_GNUC_UNUSED,void * userdata)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
browse_callback(AvahiServiceBrowser * b G_GNUC_UNUSED,AvahiIfIndex interface,AvahiProtocol protocol,AvahiBrowserEvent event,const char * name,const char * type,const char * domain,AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,void * userdata)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
domain_make_default_selection(AuiServiceDialog * d,const gchar * name,GtkTreeIter * iter)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
domain_browse_callback(AvahiDomainBrowser * b G_GNUC_UNUSED,AvahiIfIndex interface G_GNUC_UNUSED,AvahiProtocol protocol G_GNUC_UNUSED,AvahiBrowserEvent event,const char * name,AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,void * userdata)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
get_domain_name(AuiServiceDialog * d)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
start_callback(gpointer data)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
aui_service_dialog_finalize(GObject * object)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
service_row_activated_callback(GtkTreeView * tree_view G_GNUC_UNUSED,GtkTreePath * path G_GNUC_UNUSED,GtkTreeViewColumn * column G_GNUC_UNUSED,gpointer user_data)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
service_selection_changed_callback(GtkTreeSelection * selection,gpointer user_data)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
response_callback(GtkDialog * dialog,gint response,gpointer user_data)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
is_valid_domain_suffix(const gchar * n)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
domain_row_activated_callback(GtkTreeView * tree_view G_GNUC_UNUSED,GtkTreePath * path G_GNUC_UNUSED,GtkTreeViewColumn * column G_GNUC_UNUSED,gpointer user_data)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
domain_selection_changed_callback(GtkTreeSelection * selection G_GNUC_UNUSED,gpointer user_data)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
domain_entry_changed_callback(GtkEditable * editable G_GNUC_UNUSED,gpointer user_data)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
domain_button_clicked(GtkButton * button G_GNUC_UNUSED,gpointer user_data)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
aui_service_dialog_init(AuiServiceDialog * d)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
restart_browsing(AuiServiceDialog * d)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
aui_service_dialog_set_browse_service_types(AuiServiceDialog * d,const char * type,...)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
aui_service_dialog_set_browse_service_typesv(AuiServiceDialog * d,const char * const * types)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
aui_service_dialog_get_browse_service_types(AuiServiceDialog * d)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
aui_service_dialog_set_service_type_name(AuiServiceDialog * d,const gchar * type,const gchar * name)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
aui_service_dialog_set_domain(AuiServiceDialog * d,const char * domain)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
aui_service_dialog_get_domain(AuiServiceDialog * d)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
aui_service_dialog_set_service_name(AuiServiceDialog * d,const char * name)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
aui_service_dialog_get_service_name(AuiServiceDialog * d)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
aui_service_dialog_set_service_type(AuiServiceDialog * d,const char * stype)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
aui_service_dialog_get_service_type(AuiServiceDialog * d)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
aui_service_dialog_get_address(AuiServiceDialog * d)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
aui_service_dialog_get_port(AuiServiceDialog * d)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
aui_service_dialog_get_host_name(AuiServiceDialog * d)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
aui_service_dialog_get_txt_data(AuiServiceDialog * d)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
aui_service_dialog_set_resolve_service(AuiServiceDialog * d,gboolean resolve)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
aui_service_dialog_get_resolve_service(AuiServiceDialog * d)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
aui_service_dialog_set_resolve_host_name(AuiServiceDialog * d,gboolean resolve)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
aui_service_dialog_get_resolve_host_name(AuiServiceDialog * d)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
aui_service_dialog_set_address_family(AuiServiceDialog * d,AvahiProtocol proto)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
aui_service_dialog_get_address_family(AuiServiceDialog * d)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
aui_service_dialog_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)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
aui_service_dialog_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)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