remmina_avahi.c 7.96 KB
Newer Older
Antenore Gatta's avatar
Antenore Gatta committed
1 2
/*
 * Remmina - The GTK+ Remote Desktop Client
3
 * Copyright (C) 2009-2010 Vic Lee
Antenore Gatta's avatar
Antenore Gatta committed
4
 * Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo
5
 * Copyright (C) 2016-2018 Antenore Gatta, Giovanni Panozzo
Antenore Gatta's avatar
Antenore Gatta committed
6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
Antenore Gatta's avatar
Antenore Gatta committed
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
 * Boston, MA  02110-1301, USA.
 *
 *  In addition, as a special exception, the copyright holders give
 *  permission to link the code of portions of this program with the
 *  OpenSSL library under certain conditions as described in each
 *  individual source file, and distribute linked combinations
 *  including the two.
 *  You must obey the GNU General Public License in all respects
 *  for all of the code used other than OpenSSL. *  If you modify
 *  file(s) with this exception, you may extend this exception to your
 *  version of the file(s), but you are not obligated to do so. *  If you
 *  do not wish to do so, delete this exception statement from your
 *  version. *  If you delete this exception statement from all source
 *  files in the program, then also delete it here.
 *
 */
36 37 38

#include <gtk/gtk.h>
#include "config.h"
39
#include "remmina_avahi.h"
40
#include "remmina/remmina_trace_calls.h"
41 42 43 44 45 46 47 48 49

#ifdef HAVE_LIBAVAHI_CLIENT

#include <avahi-client/client.h>
#include <avahi-client/lookup.h>
#include <avahi-common/simple-watch.h>
#include <avahi-common/malloc.h>
#include <avahi-common/error.h>

Antenore Gatta's avatar
Antenore Gatta committed
50
struct _RemminaAvahiPriv {
51 52 53
	AvahiSimplePoll* simple_poll;
	AvahiClient* client;
	AvahiServiceBrowser* sb;
54 55
	guint iterate_handler;
	gboolean has_event;
56 57 58
};

static void
59
remmina_avahi_resolve_callback(
Antenore Gatta's avatar
Antenore Gatta committed
60 61 62 63 64 65 66 67 68 69 70 71 72
	AvahiServiceResolver* r,
	AVAHI_GCC_UNUSED AvahiIfIndex interface,
	AVAHI_GCC_UNUSED AvahiProtocol protocol,
	AvahiResolverEvent event,
	const char* name,
	const char* type,
	const char* domain,
	const char* host_name,
	const AvahiAddress* address,
	uint16_t port,
	AvahiStringList* txt,
	AvahiLookupResultFlags flags,
	AVAHI_GCC_UNUSED void* userdata)
73
{
74
	TRACE_CALL(__func__);
75 76
	gchar* key;
	gchar* value;
Antenore Gatta's avatar
Antenore Gatta committed
77
	RemminaAvahi* ga = (RemminaAvahi*)userdata;
78

79
	assert(r);
80

81
	ga->priv->has_event = TRUE;
82

Antenore Gatta's avatar
Antenore Gatta committed
83
	switch (event) {
84 85
	case AVAHI_RESOLVER_FAILURE:
		g_print("(remmina-applet avahi-resolver) Failed to resolve service '%s' of type '%s' in domain '%s': %s\n",
Antenore Gatta's avatar
Antenore Gatta committed
86
			name, type, domain, avahi_strerror(avahi_client_errno(avahi_service_resolver_get_client(r))));
87 88 89 90
		break;

	case AVAHI_RESOLVER_FOUND:
		key = g_strdup_printf("%s,%s,%s", name, type, domain);
Antenore Gatta's avatar
Antenore Gatta committed
91
		if (g_hash_table_lookup(ga->discovered_services, key)) {
92
			g_free(key);
93
			break;
94 95 96 97
		}
		value = g_strdup_printf("[%s]:%i", host_name, port);
		g_hash_table_insert(ga->discovered_services, key, value);
		/* key and value will be freed with g_free when the has table is freed */
98

99
		g_print("(remmina-applet avahi-resolver) Added service '%s'\n", value);
100

101
		break;
102
	}
103

104
	avahi_service_resolver_free(r);
105 106 107
}

static void
108
remmina_avahi_browse_callback(
Antenore Gatta's avatar
Antenore Gatta committed
109 110 111 112 113 114 115 116 117
	AvahiServiceBrowser* b,
	AvahiIfIndex interface,
	AvahiProtocol protocol,
	AvahiBrowserEvent event,
	const char* name,
	const char* type,
	const char* domain,
	AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
	void* userdata)
118
{
119
	TRACE_CALL(__func__);
120
	gchar* key;
Antenore Gatta's avatar
Antenore Gatta committed
121
	RemminaAvahi* ga = (RemminaAvahi*)userdata;
122

123
	assert(b);
124 125 126

	ga->priv->has_event = TRUE;

Antenore Gatta's avatar
Antenore Gatta committed
127
	switch (event) {
128 129
	case AVAHI_BROWSER_FAILURE:
		g_print("(remmina-applet avahi-browser) %s\n",
Antenore Gatta's avatar
Antenore Gatta committed
130
			avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(b))));
131
		return;
132

133 134
	case AVAHI_BROWSER_NEW:
		key = g_strdup_printf("%s,%s,%s", name, type, domain);
Antenore Gatta's avatar
Antenore Gatta committed
135
		if (g_hash_table_lookup(ga->discovered_services, key)) {
136 137
			g_free(key);
			break;
138 139 140 141 142 143
		}
		g_free(key);

		g_print("(remmina-applet avahi-browser) Found service '%s' of type '%s' in domain '%s'\n", name, type, domain);

		if (!(avahi_service_resolver_new(ga->priv->client, interface, protocol, name, type, domain,
Antenore Gatta's avatar
Antenore Gatta committed
144
			      AVAHI_PROTO_UNSPEC, 0, remmina_avahi_resolve_callback, ga))) {
145
			g_print("(remmina-applet avahi-browser) Failed to resolve service '%s': %s\n",
Antenore Gatta's avatar
Antenore Gatta committed
146
				name, avahi_strerror(avahi_client_errno(ga->priv->client)));
147 148 149 150 151 152 153 154 155 156 157 158 159
		}
		break;

	case AVAHI_BROWSER_REMOVE:
		g_print("(remmina-applet avahi-browser) Removed service '%s' of type '%s' in domain '%s'\n", name, type, domain);
		key = g_strdup_printf("%s,%s,%s", name, type, domain);
		g_hash_table_remove(ga->discovered_services, key);
		g_free(key);
		break;

	case AVAHI_BROWSER_ALL_FOR_NOW:
	case AVAHI_BROWSER_CACHE_EXHAUSTED:
		break;
160
	}
161 162
}

163
static void remmina_avahi_client_callback(AvahiClient* c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata)
164
{
165
	TRACE_CALL(__func__);
Antenore Gatta's avatar
Antenore Gatta committed
166
	RemminaAvahi* ga = (RemminaAvahi*)userdata;
167

168
	ga->priv->has_event = TRUE;
169

Antenore Gatta's avatar
Antenore Gatta committed
170
	if (state == AVAHI_CLIENT_FAILURE) {
171
		g_print("(remmina-applet avahi) Server connection failure: %s\n", avahi_strerror(avahi_client_errno(c)));
172
	}
173 174
}

175
static gboolean remmina_avahi_iterate(RemminaAvahi* ga)
176
{
177
	TRACE_CALL(__func__);
Antenore Gatta's avatar
Antenore Gatta committed
178
	while (TRUE) {
179 180 181 182 183 184 185 186
		/* Call the iteration until no further events */
		ga->priv->has_event = FALSE;
		avahi_simple_poll_iterate(ga->priv->simple_poll, 0);
		if (!ga->priv->has_event)
			break;
	}

	return TRUE;
187 188
}

189
RemminaAvahi* remmina_avahi_new(void)
190
{
191
	TRACE_CALL(__func__);
192
	RemminaAvahi* ga;
193 194 195 196 197 198 199 200 201 202 203 204

	ga = g_new(RemminaAvahi, 1);
	ga->discovered_services = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
	ga->started = FALSE;
	ga->priv = g_new(RemminaAvahiPriv, 1);
	ga->priv->simple_poll = NULL;
	ga->priv->client = NULL;
	ga->priv->sb = NULL;
	ga->priv->iterate_handler = 0;
	ga->priv->has_event = FALSE;

	return ga;
205 206
}

207
void remmina_avahi_start(RemminaAvahi* ga)
208
{
209
	TRACE_CALL(__func__);
210 211 212 213 214 215 216 217
	int error;

	if (ga->started)
		return;

	ga->started = TRUE;

	ga->priv->simple_poll = avahi_simple_poll_new();
Antenore Gatta's avatar
Antenore Gatta committed
218
	if (!ga->priv->simple_poll) {
219 220 221 222 223
		g_print("Failed to create simple poll object.\n");
		return;
	}

	ga->priv->client = avahi_client_new(avahi_simple_poll_get(ga->priv->simple_poll), 0, remmina_avahi_client_callback, ga,
Antenore Gatta's avatar
Antenore Gatta committed
224 225
		&error);
	if (!ga->priv->client) {
226 227 228 229
		g_print("Failed to create client: %s\n", avahi_strerror(error));
		return;
	}

230
	/** @todo Customize the default domain here */
231
	ga->priv->sb = avahi_service_browser_new(ga->priv->client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_rfb._tcp", NULL, 0,
Antenore Gatta's avatar
Antenore Gatta committed
232 233
		remmina_avahi_browse_callback, ga);
	if (!ga->priv->sb) {
234 235 236 237
		g_print("Failed to create service browser: %s\n", avahi_strerror(avahi_client_errno(ga->priv->client)));
		return;
	}

Antenore Gatta's avatar
Antenore Gatta committed
238
	ga->priv->iterate_handler = g_timeout_add(5000, (GSourceFunc)remmina_avahi_iterate, ga);
239 240
}

241
void remmina_avahi_stop(RemminaAvahi* ga)
242
{
243
	TRACE_CALL(__func__);
244
	g_hash_table_remove_all(ga->discovered_services);
Antenore Gatta's avatar
Antenore Gatta committed
245
	if (ga->priv->iterate_handler) {
246 247 248
		g_source_remove(ga->priv->iterate_handler);
		ga->priv->iterate_handler = 0;
	}
Antenore Gatta's avatar
Antenore Gatta committed
249
	if (ga->priv->sb) {
250 251 252
		avahi_service_browser_free(ga->priv->sb);
		ga->priv->sb = NULL;
	}
Antenore Gatta's avatar
Antenore Gatta committed
253
	if (ga->priv->client) {
254 255 256
		avahi_client_free(ga->priv->client);
		ga->priv->client = NULL;
	}
Antenore Gatta's avatar
Antenore Gatta committed
257
	if (ga->priv->simple_poll) {
258 259 260 261
		avahi_simple_poll_free(ga->priv->simple_poll);
		ga->priv->simple_poll = NULL;
	}
	ga->started = FALSE;
262 263
}

264
void remmina_avahi_free(RemminaAvahi* ga)
265
{
266
	TRACE_CALL(__func__);
267 268
	if (ga == NULL)
		return;
269

270
	remmina_avahi_stop(ga);
271

272 273 274
	g_free(ga->priv);
	g_hash_table_destroy(ga->discovered_services);
	g_free(ga);
275 276 277 278
}

#else

279
RemminaAvahi* remmina_avahi_new(void)
280
{
281
	TRACE_CALL(__func__);
282
	return NULL;
283 284
}

285
void remmina_avahi_start(RemminaAvahi* ga)
286
{
287
	TRACE_CALL(__func__);
288 289
}

290
void remmina_avahi_stop(RemminaAvahi* ga)
291
{
292
	TRACE_CALL(__func__);
293 294
}

295
void remmina_avahi_free(RemminaAvahi* ga)
296
{
297
	TRACE_CALL(__func__);
298 299 300 301
}

#endif