]> git.meshlink.io Git - catta/blob - avahi-discover/main.c
* cleaned configure.ac
[catta] / avahi-discover / main.c
1 #ifdef HAVE_CONFIG_H
2 #include <config.h>
3 #endif
4
5 #include <string.h>
6
7 #include <gtk/gtk.h>
8 #include <glade/glade.h>
9 #include <avahi-core/core.h>
10 #include <avahi-common/strlst.h>
11
12 struct ServiceType;
13
14 struct Service {
15     struct ServiceType *service_type;
16     gchar *service_name;
17     gchar *domain_name;
18
19     gint interface;
20     guchar protocol;
21
22     GtkTreeRowReference *tree_ref;
23 };
24
25 struct ServiceType {
26     gchar *service_type;
27     AvahiServiceBrowser *browser;
28
29     GList *services;
30     GtkTreeRowReference *tree_ref;
31 };
32
33 static GtkWidget *main_window = NULL;
34 static GtkTreeView *tree_view = NULL;
35 static GtkTreeStore *tree_store = NULL;
36 static GtkLabel *info_label = NULL;
37 static AvahiServer *server = NULL;
38 static AvahiServiceTypeBrowser *service_type_browser = NULL;
39 static GHashTable *service_type_hash_table = NULL;
40 static AvahiServiceResolver *service_resolver = NULL;
41 static struct Service *current_service = NULL;
42
43 /* very, very ugly: just import these two internal but useful functions from libavahi-core by hand */
44 guint avahi_domain_hash(const gchar *s);
45 gboolean avahi_domain_equal(const gchar *a, const gchar *b);
46
47 static struct Service *get_service(const gchar *service_type, const gchar *service_name, const gchar*domain_name, gint interface, guchar protocol) {
48     struct ServiceType *st;
49     GList *l;
50
51     if (!(st = g_hash_table_lookup(service_type_hash_table, service_type)))
52         return NULL;
53
54     for (l = st->services; l; l = l->next) {
55         struct Service *s = l->data;
56         
57         if (s->interface == interface &&
58             s->protocol == protocol &&
59             avahi_domain_equal(s->service_name, service_name) &&
60             avahi_domain_equal(s->domain_name, domain_name))
61             return s;
62     }
63
64     return NULL;
65 }
66
67 static void free_service(struct Service *s) {
68     GtkTreeIter iter;
69     GtkTreePath *path;
70
71     if (current_service == s) {
72         current_service = NULL;
73
74         if (service_resolver) {
75             avahi_service_resolver_free(service_resolver);
76             service_resolver = NULL;
77         }
78  
79         gtk_label_set_text(info_label, "<i>Service removed</i>");
80     }
81     
82     s->service_type->services = g_list_remove(s->service_type->services, s);
83
84     path = gtk_tree_row_reference_get_path(s->tree_ref);
85     gtk_tree_model_get_iter(GTK_TREE_MODEL(tree_store), &iter, path);
86     gtk_tree_path_free(path);
87     
88     gtk_tree_store_remove(tree_store, &iter);
89
90     gtk_tree_row_reference_free(s->tree_ref);
91
92     g_free(s->service_name);
93     g_free(s->domain_name);
94     g_free(s);
95 }
96
97 static void service_browser_callback(AvahiServiceBrowser *b, gint interface, guchar protocol, AvahiBrowserEvent event, const gchar *service_name, const gchar *service_type, const gchar *domain_name, gpointer userdata) {
98
99     if (event == AVAHI_BROWSER_NEW) {
100         struct Service *s;
101         GtkTreeIter iter, piter;
102         GtkTreePath *path, *ppath;
103         gchar iface[256];
104         
105         s = g_new(struct Service, 1);
106         s->service_name = g_strdup(service_name);
107         s->domain_name = g_strdup(domain_name);
108         s->interface = interface;
109         s->protocol = protocol;
110         s->service_type = g_hash_table_lookup(service_type_hash_table, service_type);
111         g_assert(s->service_type);
112         
113         s->service_type->services = g_list_prepend(s->service_type->services, s);
114
115         ppath = gtk_tree_row_reference_get_path(s->service_type->tree_ref);
116         gtk_tree_model_get_iter(GTK_TREE_MODEL(tree_store), &piter, ppath);
117
118         snprintf(iface, sizeof(iface), "#%i %s", interface, protocol == AF_INET ? "IPv4" : "IPv6");
119
120         gtk_tree_store_append(tree_store, &iter, &piter);
121         gtk_tree_store_set(tree_store, &iter, 0, s->service_name, 1, iface, 2, s, -1);
122
123         path = gtk_tree_model_get_path(GTK_TREE_MODEL(tree_store), &iter);
124         s->tree_ref = gtk_tree_row_reference_new(GTK_TREE_MODEL(tree_store), path);
125         gtk_tree_path_free(path);
126
127         gtk_tree_view_expand_row(tree_view, ppath, FALSE);
128         gtk_tree_path_free(ppath);
129
130     
131     } else if (event == AVAHI_BROWSER_REMOVE) {
132         struct Service* s;
133
134         if ((s = get_service(service_type, service_name, domain_name, interface, protocol)))
135             free_service(s);
136     }
137 }
138
139 static void service_type_browser_callback(AvahiServiceTypeBrowser *b, gint interface, guchar protocol, AvahiBrowserEvent event, const gchar *service_type, const gchar *domain, gpointer userdata) {
140     struct ServiceType *st;
141     GtkTreePath *path;
142     GtkTreeIter iter;
143
144     if (event != AVAHI_BROWSER_NEW)
145         return;
146
147     if (g_hash_table_lookup(service_type_hash_table, service_type))
148         return;
149
150     st = g_new(struct ServiceType, 1);
151     st->service_type = g_strdup(service_type);
152     st->services = NULL;
153     
154     gtk_tree_store_append(tree_store, &iter, NULL);
155     gtk_tree_store_set(tree_store, &iter, 0, st->service_type, 1, "", 2, NULL, -1);
156
157     path = gtk_tree_model_get_path(GTK_TREE_MODEL(tree_store), &iter);
158     st->tree_ref = gtk_tree_row_reference_new(GTK_TREE_MODEL(tree_store), path);
159     gtk_tree_path_free(path);
160
161     g_hash_table_insert(service_type_hash_table, st->service_type, st);
162
163     st->browser = avahi_service_browser_new(server, -1, AF_UNSPEC, st->service_type, domain, service_browser_callback, NULL);
164 }
165
166 static void update_label(struct Service *s, const gchar *hostname, const AvahiAddress *a, guint16 port, AvahiStringList *txt) {
167     gchar t[512], address[64], *txt_s;
168
169     if (a && hostname) {
170         char na[256];
171         avahi_address_snprint(na, sizeof(na), a);
172         snprintf(address, sizeof(address), "%s/%s:%u", hostname, na, port);
173         txt_s = avahi_string_list_to_string(txt);
174     } else {
175         snprintf(address, sizeof(address), "<i>n/a</i>");
176         txt_s = g_strdup("<i>n/a</i>");
177     }
178     
179     snprintf(t, sizeof(t),
180              "<b>Service Type:</b> %s\n"
181              "<b>Service Name:</b> %s\n"
182              "<b>Domain Name:</b> %s\n"
183              "<b>Interface:</b> %i %s\n"
184              "<b>Address:</b> %s\n"
185              "<b>TXT Data:</b> %s",
186              s->service_type->service_type,
187              s->service_name,
188              s->domain_name,
189              s->interface,
190              s->protocol == AF_INET ? "IPv4" : "IPv6",
191              address,
192              txt_s);
193
194     gtk_label_set_markup(info_label, t);
195
196     g_free(txt_s);
197 }
198
199 static struct Service *get_service_on_cursor(void) {
200     GtkTreePath *path;
201     struct Service *s;
202     GtkTreeIter iter;
203     
204     gtk_tree_view_get_cursor(tree_view, &path, NULL);
205     gtk_tree_model_get_iter(GTK_TREE_MODEL(tree_store), &iter, path);
206     gtk_tree_model_get(GTK_TREE_MODEL(tree_store), &iter, 2, &s, -1);
207     gtk_tree_path_free(path);
208
209     return s;
210 }
211
212
213 static void service_resolver_callback(AvahiServiceResolver *r, gint interface, guchar protocol, AvahiResolverEvent event, const gchar *name, const gchar *type, const gchar *domain, const gchar *host_name, const AvahiAddress *a, guint16 port, AvahiStringList *txt, gpointer userdata) {
214     struct Service *s;
215     g_assert(r);
216
217     if (!(s = get_service_on_cursor()) || userdata != s) {
218         g_assert(r == service_resolver);
219         avahi_service_resolver_free(service_resolver);
220         service_resolver = NULL;
221     }
222
223     if (event == AVAHI_RESOLVER_TIMEOUT)
224         gtk_label_set_markup(info_label, "<i>Failed to resolve.</i>");
225     else 
226         update_label(s, host_name, a, port, txt);
227 }
228
229 static void tree_view_on_cursor_changed(GtkTreeView *tv, gpointer userdata) {
230     struct Service *s;
231     
232     if (!(s = get_service_on_cursor()))
233         return;
234
235     if (service_resolver)
236         avahi_service_resolver_free(service_resolver);
237
238     update_label(s, NULL, NULL, 0, NULL);
239
240     service_resolver = avahi_service_resolver_new(server, s->interface, s->protocol, s->service_name, s->service_type->service_type, s->domain_name, AF_UNSPEC, service_resolver_callback, s);
241 }
242
243 gboolean main_window_on_delete_event(GtkWidget *widget, GdkEvent *event, gpointer user_data) {
244     gtk_main_quit();
245     return FALSE;
246 }
247
248 int main(int argc, char *argv[]) {
249     GladeXML *xml;
250     AvahiServerConfig config;
251     GtkTreeViewColumn *c;
252
253     gtk_init(&argc, &argv);
254     glade_init();
255
256     xml = glade_xml_new(AVAHI_INTERFACES_DIR"avahi-discover.glade", NULL, NULL);
257     main_window = glade_xml_get_widget(xml, "main_window");
258     g_signal_connect(main_window, "delete-event", (GCallback) main_window_on_delete_event, NULL);
259     
260     tree_view = GTK_TREE_VIEW(glade_xml_get_widget(xml, "tree_view"));
261     g_signal_connect(GTK_WIDGET(tree_view), "cursor-changed", (GCallback) tree_view_on_cursor_changed, NULL);
262
263     info_label = GTK_LABEL(glade_xml_get_widget(xml, "info_label"));
264
265     tree_store = gtk_tree_store_new(3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER);
266     gtk_tree_view_set_model(tree_view, GTK_TREE_MODEL(tree_store));
267     gtk_tree_view_insert_column_with_attributes(tree_view, -1, "Name", gtk_cell_renderer_text_new(), "text", 0, NULL);
268     gtk_tree_view_insert_column_with_attributes(tree_view, -1, "Interface", gtk_cell_renderer_text_new(), "text", 1, NULL);
269
270     gtk_tree_view_column_set_resizable(c = gtk_tree_view_get_column(tree_view, 0), TRUE);
271     gtk_tree_view_column_set_sizing(c, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
272     gtk_tree_view_column_set_expand(c, TRUE);
273     
274     service_type_hash_table = g_hash_table_new((GHashFunc) avahi_domain_hash, (GEqualFunc) avahi_domain_equal);
275     
276     avahi_server_config_init(&config);
277     config.publish_hinfo = config.publish_addresses = config.publish_domain = config.publish_workstation = FALSE;
278     server = avahi_server_new(NULL, &config, NULL, NULL);
279     avahi_server_config_free(&config);
280
281     service_type_browser = avahi_service_type_browser_new(server, -1, AF_UNSPEC, argc >= 2 ? argv[1] : NULL, service_type_browser_callback, NULL);
282
283     gtk_main();
284
285     avahi_server_free(server);
286
287     return 0;
288 }