Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-3.0-or-later
2 : : // SPDX-FileCopyrightText: Andy Holmes <andrew.g.r.holmes@gmail.com>
3 : :
4 : : #define G_LOG_DOMAIN "valent-lan-dnssd"
5 : :
6 : : #include "config.h"
7 : :
8 : : #include <gio/gio.h>
9 : : #include <valent.h>
10 : :
11 : : #include "valent-lan-dnssd.h"
12 : : #include "valent-lan-utils.h"
13 : :
14 : : #define AVAHI_DBUS_NAME "org.freedesktop.Avahi"
15 : : #define AVAHI_SERVER2_PATH "/"
16 : : #define AVAHI_SERVER2_IFACE "org.freedesktop.Avahi.Server2"
17 : : #define AVAHI_ENTRY_GROUP_IFACE "org.freedesktop.Avahi.EntryGroup"
18 : : #define AVAHI_SERVICE_BROWSER_IFACE "org.freedesktop.Avahi.ServiceBrowser"
19 : :
20 : : #define KDECONNECT_UDP_SERVICE_TYPE "_kdeconnect._udp"
21 : :
22 : : /*< private >
23 : : * ValentAvahiAddressEnumerator:
24 : : *
25 : : * A [class@Gio.SocketAddressEnumerator] implementation that uses Avahi to
26 : : * resolve candidates.
27 : : */
28 : : #define VALENT_TYPE_AVAHI_ADDRESS_ENUMERATOR (valent_avahi_address_enumerator_get_type())
29 [ + - + - : 1 : G_DECLARE_FINAL_TYPE (ValentAvahiAddressEnumerator, valent_avahi_address_enumerator, VALENT, AVAHI_ADDRESS_ENUMERATOR, GSocketAddressEnumerator)
- + ]
30 : :
31 : : struct _ValentAvahiAddressEnumerator
32 : : {
33 : : GSocketAddressEnumerator parent_instance;
34 : :
35 : : GDBusConnection *connection;
36 : : GPtrArray *items;
37 : : unsigned int position;
38 : : };
39 : :
40 [ + + + - ]: 4 : G_DEFINE_FINAL_TYPE (ValentAvahiAddressEnumerator, valent_avahi_address_enumerator, G_TYPE_SOCKET_ADDRESS_ENUMERATOR)
41 : :
42 : : static void
43 : 1 : valent_avahi_address_enumerator_next_cb (GDBusConnection *connection,
44 : : GAsyncResult *result,
45 : : gpointer user_data)
46 : : {
47 : 2 : g_autoptr (GTask) task = G_TASK (g_steal_pointer (&user_data));
48 [ + - - - ]: 1 : g_autoptr (GVariant) reply = NULL;
49 [ - - ]: 1 : g_autoptr (GSocketAddress) ret = NULL;
50 : 1 : int interface = 0;
51 : 1 : int protocol = 0;
52 : 1 : const char *name = NULL;
53 : 1 : const char *type = NULL;
54 : 1 : const char *domain = NULL;
55 : 1 : const char *host = NULL;
56 : 1 : int aprotocol = 0;
57 : 1 : const char *address = NULL;
58 : 1 : uint16_t port = 0;
59 [ - - ]: 1 : g_autoptr (GVariant) txt = NULL;
60 : 1 : uint32_t flags = 0;
61 : 1 : GError *error = NULL;
62 : :
63 : 1 : reply = g_dbus_connection_call_finish (connection, result, &error);
64 [ - + ]: 1 : if (reply == NULL)
65 : : {
66 : 0 : g_dbus_error_strip_remote_error (error);
67 : 0 : g_task_return_error (task, g_steal_pointer (&error));
68 : 0 : return;
69 : : }
70 : :
71 : 1 : g_variant_get (reply,
72 : : "(ii&s&s&s&si&sq@aayu)",
73 : : &interface,
74 : : &protocol,
75 : : &name,
76 : : &type,
77 : : &domain,
78 : : &host,
79 : : &aprotocol,
80 : : &address,
81 : : &port,
82 : : &txt,
83 : : &flags);
84 : :
85 : 1 : ret = g_inet_socket_address_new_from_string (address, port);
86 [ - + ]: 1 : if (ret == NULL)
87 : : {
88 : 0 : g_task_return_new_error (task,
89 : : G_IO_ERROR,
90 : : G_IO_ERROR_FAILED,
91 : : "Failed to create socket address for %s:%u",
92 : : address, port);
93 : 0 : return;
94 : : }
95 : :
96 [ + - + - ]: 1 : g_task_return_pointer (task, g_steal_pointer (&ret), g_object_unref);
97 : : }
98 : :
99 : : static void
100 : 1 : valent_avahi_address_enumerator_next_async (GSocketAddressEnumerator *enumerator,
101 : : GCancellable *cancellable,
102 : : GAsyncReadyCallback callback,
103 : : gpointer user_data)
104 : : {
105 : 1 : ValentAvahiAddressEnumerator *self = VALENT_AVAHI_ADDRESS_ENUMERATOR (enumerator);
106 : 1 : g_autoptr (GTask) task = NULL;
107 : 1 : GVariant *parameters = NULL;
108 : 1 : int interface = 0;
109 : 1 : int protocol = 0;
110 : 1 : const char *name = 0;
111 : 1 : const char *type = 0;
112 : 1 : const char *domain = 0;
113 : 1 : uint32_t flags = 0;
114 : :
115 [ - + ]: 1 : g_assert (VALENT_IS_AVAHI_ADDRESS_ENUMERATOR (self));
116 : :
117 : 1 : task = g_task_new (self, cancellable, callback, user_data);
118 [ + - ]: 1 : g_task_set_source_tag (task, valent_avahi_address_enumerator_next_async);
119 : :
120 [ - + ]: 1 : if (self->position >= self->items->len)
121 : : {
122 : 0 : g_task_return_pointer (task, NULL, NULL);
123 [ # # ]: 0 : return;
124 : : }
125 : :
126 : : /* These are the parameters from an ItemNew emission
127 : : */
128 : 1 : parameters = g_ptr_array_index (self->items, self->position++);
129 : 1 : g_variant_get (parameters,
130 : : "(ii&s&s&su)",
131 : : &interface,
132 : : &protocol,
133 : : &name,
134 : : &type,
135 : : &domain,
136 : : &flags);
137 : :
138 [ + - + - ]: 1 : g_dbus_connection_call (self->connection,
139 : : AVAHI_DBUS_NAME,
140 : : AVAHI_SERVER2_PATH,
141 : : AVAHI_SERVER2_IFACE,
142 : : "ResolveService",
143 : : g_variant_new ("(iisssiu)",
144 : : interface,
145 : : protocol,
146 : : name,
147 : : type,
148 : : domain,
149 : : -1, // aprotocol: AVAHI_PROTO_UNSPEC
150 : : 0), // flags: AvahiLookupFlags
151 : : G_VARIANT_TYPE ("(iissssisqaayu)"),
152 : : G_DBUS_CALL_FLAGS_NO_AUTO_START,
153 : : -1,
154 : : cancellable,
155 : : (GAsyncReadyCallback)valent_avahi_address_enumerator_next_cb,
156 : : g_object_ref (task));
157 : : }
158 : :
159 : : static void
160 : 1 : valent_avahi_address_enumerator_finalize (GObject *object)
161 : : {
162 : 1 : ValentAvahiAddressEnumerator *self = VALENT_AVAHI_ADDRESS_ENUMERATOR (object);
163 : :
164 [ + - ]: 1 : g_clear_object (&self->connection);
165 [ + - ]: 1 : g_clear_pointer (&self->items, g_ptr_array_unref);
166 : :
167 : 1 : G_OBJECT_CLASS (valent_avahi_address_enumerator_parent_class)->finalize (object);
168 : 1 : }
169 : :
170 : : static void
171 : 1 : valent_avahi_address_enumerator_class_init (ValentAvahiAddressEnumeratorClass *klass)
172 : : {
173 : 1 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
174 : 1 : GSocketAddressEnumeratorClass *enumerator_class = G_SOCKET_ADDRESS_ENUMERATOR_CLASS (klass);
175 : :
176 : 1 : object_class->finalize = valent_avahi_address_enumerator_finalize;
177 : :
178 : 1 : enumerator_class->next_async = valent_avahi_address_enumerator_next_async;
179 : : }
180 : :
181 : : static void
182 : 1 : valent_avahi_address_enumerator_init (ValentAvahiAddressEnumerator *self)
183 : : {
184 : 1 : }
185 : :
186 : : /*< private >
187 : : * ValentAvahiConnectable:
188 : : *
189 : : * A [iface@Gio.SocketConnectable] implementation that aggregates the
190 : : * candidates for a service discovered by Avahi.
191 : : */
192 : : #define VALENT_TYPE_AVAHI_CONNECTABLE (valent_avahi_connectable_get_type())
193 [ + - + - : 3 : G_DECLARE_FINAL_TYPE (ValentAvahiConnectable, valent_avahi_connectable, VALENT, AVAHI_CONNECTABLE, GObject)
- + ]
194 : :
195 : : struct _ValentAvahiConnectable
196 : : {
197 : : GObject parent_instance;
198 : :
199 : : GDBusConnection *connection;
200 : : GPtrArray *items;
201 : : char *service_name;
202 : : };
203 : :
204 : : static void g_socket_connectable_iface_init (GSocketConnectableIface *iface);
205 : :
206 [ + + + - ]: 10 : G_DEFINE_FINAL_TYPE_WITH_CODE (ValentAvahiConnectable, valent_avahi_connectable, G_TYPE_OBJECT,
207 : : G_IMPLEMENT_INTERFACE (G_TYPE_SOCKET_CONNECTABLE, g_socket_connectable_iface_init))
208 : :
209 : : static GSocketAddressEnumerator *
210 : 1 : valent_avahi_connectable_enumerate (GSocketConnectable *connectable)
211 : : {
212 : 1 : ValentAvahiConnectable *self = VALENT_AVAHI_CONNECTABLE (connectable);
213 : 1 : ValentAvahiAddressEnumerator *enumerator = NULL;
214 : :
215 [ - + ]: 1 : g_assert (VALENT_IS_AVAHI_CONNECTABLE (self));
216 : :
217 : 1 : enumerator = g_object_new (VALENT_TYPE_AVAHI_ADDRESS_ENUMERATOR, NULL);
218 : 1 : enumerator->connection = g_object_ref (self->connection);
219 : 1 : enumerator->items = g_ptr_array_ref (self->items);
220 : :
221 : 1 : return G_SOCKET_ADDRESS_ENUMERATOR (enumerator);
222 : : }
223 : :
224 : : static char *
225 : 2 : valent_avahi_connectable_to_string (GSocketConnectable *connectable)
226 : : {
227 : 2 : ValentAvahiConnectable *self = VALENT_AVAHI_CONNECTABLE (connectable);
228 : :
229 [ - + ]: 2 : g_assert (VALENT_IS_AVAHI_CONNECTABLE (self));
230 : :
231 [ - + ]: 2 : return g_strdup (self->service_name);
232 : : }
233 : :
234 : : static void
235 : 2 : g_socket_connectable_iface_init (GSocketConnectableIface *iface)
236 : : {
237 : 2 : iface->enumerate = valent_avahi_connectable_enumerate;
238 : 2 : iface->to_string = valent_avahi_connectable_to_string;
239 : 2 : }
240 : :
241 : : static void
242 : 3 : valent_avahi_connectable_finalize (GObject *object)
243 : : {
244 : 3 : ValentAvahiConnectable *self = VALENT_AVAHI_CONNECTABLE (object);
245 : :
246 [ + - ]: 3 : g_clear_object (&self->connection);
247 [ + - ]: 3 : g_clear_pointer (&self->items, g_ptr_array_unref);
248 [ + - ]: 3 : g_clear_pointer (&self->service_name, g_free);
249 : :
250 : 3 : G_OBJECT_CLASS (valent_avahi_connectable_parent_class)->finalize (object);
251 : 3 : }
252 : :
253 : : static void
254 : 2 : valent_avahi_connectable_class_init (ValentAvahiConnectableClass *klass)
255 : : {
256 : 2 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
257 : :
258 : 2 : object_class->finalize = valent_avahi_connectable_finalize;
259 : : }
260 : :
261 : : static void
262 : 3 : valent_avahi_connectable_init (ValentAvahiConnectable *self)
263 : : {
264 : 3 : self->items = g_ptr_array_new_with_free_func ((GDestroyNotify)g_variant_unref);
265 : 3 : }
266 : :
267 : : /**
268 : : * ValentLanDNSSD:
269 : : *
270 : : * A simple DNS-SD manager.
271 : : *
272 : : * `ValentLanDNSSD` implements [iface@Gio.ListModel], representing discovered
273 : : * services as [class@Gio.SocketConnectable] objects.
274 : : *
275 : : * If the [property@ValentLanDNSSD:identity] property is set to a KDE Connect
276 : : * identity packet (`kdeconnect.identity`), it will export a service with the
277 : : * type `_kdeconnect._udp`.
278 : : */
279 : :
280 : : struct _ValentLanDNSSD
281 : : {
282 : : ValentObject parent_instance;
283 : :
284 : : JsonNode *identity;
285 : : char *service_type;
286 : :
287 : : char *name;
288 : : uint16_t port;
289 : : GVariant *txt;
290 : :
291 : : GDBusConnection *connection;
292 : : GCancellable *cancellable;
293 : : unsigned int watcher_id;
294 : :
295 : : unsigned int server_state;
296 : : unsigned int server_state_id;
297 : : char *entry_group_path;
298 : : unsigned int entry_group_state;
299 : : unsigned int entry_group_state_id;
300 : : char *service_browser_path;
301 : : unsigned int service_browser_event_id;
302 : :
303 : : /* list model */
304 : : GPtrArray *items;
305 : : GHashTable *pending;
306 : : unsigned int pending_id;
307 : : };
308 : :
309 : : static void g_list_model_iface_init (GListModelInterface *iface);
310 : :
311 [ + + + - ]: 77 : G_DEFINE_FINAL_TYPE_WITH_CODE (ValentLanDNSSD, valent_lan_dnssd, VALENT_TYPE_OBJECT,
312 : : G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, g_list_model_iface_init))
313 : :
314 : : typedef enum {
315 : : PROP_IDENTITY = 1,
316 : : PROP_SERVICE_TYPE,
317 : : } ValentLanDNSSDProperty;
318 : :
319 : : static GParamSpec *properties[PROP_SERVICE_TYPE + 1] = { NULL, };
320 : :
321 : :
322 : : static inline GVariant *
323 : 15 : txt_new_str (const char *name,
324 : : const char *value)
325 : : {
326 : 30 : g_autoptr (GBytes) bytes = NULL;
327 [ + - ]: 15 : g_autofree char *key = NULL;
328 : 15 : size_t key_len;
329 : :
330 : 15 : key = g_strdup_printf ("%s=%s", name, value);
331 : 15 : key_len = strlen (key);
332 : 15 : bytes = g_bytes_new_take (g_steal_pointer (&key), key_len);
333 : :
334 : 15 : return g_variant_new_from_bytes (G_VARIANT_TYPE_BYTESTRING, bytes, TRUE);
335 : : }
336 : :
337 : : static inline GVariant *
338 : 5 : txt_new_uint (const char *name,
339 : : uint32_t value)
340 : : {
341 : 10 : g_autoptr (GBytes) bytes = NULL;
342 [ + - ]: 5 : g_autofree char *key = NULL;
343 : 5 : size_t key_len;
344 : :
345 : 5 : key = g_strdup_printf ("%s=%u", name, value);
346 : 5 : key_len = strlen (key);
347 : 5 : bytes = g_bytes_new_take (g_steal_pointer (&key), key_len);
348 : :
349 : 5 : return g_variant_new_from_bytes (G_VARIANT_TYPE_BYTESTRING, bytes, TRUE);
350 : : }
351 : :
352 : : static inline GWeakRef *
353 : 12 : weak_ref_new (gpointer object)
354 : : {
355 : 12 : GWeakRef *weak_ref;
356 : :
357 [ + - - + ]: 12 : g_assert (object == NULL || G_IS_OBJECT (object));
358 : :
359 : 12 : weak_ref = g_new0 (GWeakRef, 1);
360 : 12 : g_weak_ref_init (weak_ref, object);
361 : :
362 : 12 : return g_steal_pointer (&weak_ref);
363 : : }
364 : :
365 : : static inline void
366 : 9 : weak_ref_free (gpointer data)
367 : : {
368 : 9 : GWeakRef *weak_ref = data;
369 : :
370 : 9 : g_weak_ref_clear (weak_ref);
371 : 9 : g_free (weak_ref);
372 : 9 : }
373 : :
374 : : /*
375 : : * GListModel
376 : : */
377 : : static gpointer
378 : 3 : valent_lan_dnssd_get_item (GListModel *list,
379 : : unsigned int position)
380 : : {
381 : 3 : ValentLanDNSSD *self = VALENT_LAN_DNSSD (list);
382 : :
383 [ - + ]: 3 : g_assert (VALENT_IS_LAN_DNSSD (self));
384 : :
385 [ + - ]: 3 : if G_UNLIKELY (position >= self->items->len)
386 : : return NULL;
387 : :
388 : 3 : return g_object_ref (g_ptr_array_index (self->items, position));
389 : : }
390 : :
391 : : static GType
392 : 0 : valent_lan_dnssd_get_item_type (GListModel *list)
393 : : {
394 : 0 : return G_TYPE_SOCKET_CONNECTABLE;
395 : : }
396 : :
397 : : static unsigned int
398 : 0 : valent_lan_dnssd_get_n_items (GListModel *list)
399 : : {
400 : 0 : ValentLanDNSSD *self = VALENT_LAN_DNSSD (list);
401 : 0 : unsigned int ret = 0;
402 : :
403 [ # # ]: 0 : g_assert (VALENT_IS_LAN_DNSSD (self));
404 : :
405 : 0 : ret = self->items->len;
406 : :
407 : 0 : return ret;
408 : : }
409 : :
410 : : static void
411 : 2 : g_list_model_iface_init (GListModelInterface *iface)
412 : : {
413 : 2 : iface->get_item = valent_lan_dnssd_get_item;
414 : 2 : iface->get_item_type = valent_lan_dnssd_get_item_type;
415 : 2 : iface->get_n_items = valent_lan_dnssd_get_n_items;
416 : 2 : }
417 : :
418 : : /*
419 : : * Avahi D-Bus Service
420 : : *
421 : : * See Also:
422 : : * - https://github.com/lathiat/avahi/blob/master/avahi-daemon/org.freedesktop.Avahi.Server.xml
423 : : * - https://github.com/lathiat/avahi/blob/master/avahi-daemon/org.freedesktop.Avahi.ServiceBrowser.xml
424 : : * - https://github.com/lathiat/avahi/blob/master/avahi-daemon/org.freedesktop.Avahi.EntryGroup.xml
425 : : */
426 : : enum {
427 : : _AVAHI_SERVER_INVALID,
428 : : _AVAHI_SERVER_REGISTERING,
429 : : _AVAHI_SERVER_RUNNING,
430 : : _AVAHI_SERVER_COLLISION,
431 : : _AVAHI_SERVER_FAILURE,
432 : : };
433 : :
434 : : enum {
435 : : _AVAHI_ENTRY_GROUP_UNCOMMITTED,
436 : : _AVAHI_ENTRY_GROUP_REGISTERING,
437 : : _AVAHI_ENTRY_GROUP_ESTABLISHED,
438 : : _AVAHI_ENTRY_GROUP_COLLISION,
439 : : _AVAHI_ENTRY_GROUP_FAILURE,
440 : : };
441 : :
442 : : static void _avahi_client_connect (ValentLanDNSSD *self);
443 : : static void _avahi_client_disconnect (ValentLanDNSSD *self);
444 : : static void _avahi_entry_group_new (ValentLanDNSSD *self);
445 : : static void _avahi_entry_group_add_service (ValentLanDNSSD *self);
446 : : static void _avahi_entry_group_commit (ValentLanDNSSD *self);
447 : : static void _avahi_entry_group_reset (ValentLanDNSSD *self);
448 : : static void _avahi_service_browser_prepare (ValentLanDNSSD *self);
449 : :
450 : :
451 : : /*
452 : : * Entry Group
453 : : *
454 : : * These functions export a DNS-SD service based on the content of
455 : : * [property@ValentLanDNSSD:identity].
456 : : */
457 : : static void
458 : 20 : _avahi_entry_group_state_changed (GDBusConnection *connection,
459 : : const char *sender_name,
460 : : const char *object_path,
461 : : const char *interface_name,
462 : : const char *signal_name,
463 : : GVariant *parameters,
464 : : gpointer user_data)
465 : : {
466 : 40 : g_autoptr (ValentLanDNSSD) self = g_weak_ref_get ((GWeakRef *)user_data);
467 : 20 : const char *error = NULL;
468 : :
469 [ + - - + ]: 20 : g_assert (self == NULL || VALENT_IS_LAN_DNSSD (self));
470 [ + - ]: 20 : g_assert (g_str_equal (signal_name, "StateChanged"));
471 : :
472 [ + - - + ]: 20 : if (self == NULL || valent_object_in_destruction (VALENT_OBJECT (self)))
473 : 0 : return;
474 : :
475 : 20 : g_variant_get (parameters, "(i&s)", &self->entry_group_state, &error);
476 : :
477 : 20 : VALENT_NOTE ("[%i] %s", self->entry_group_state, error);
478 : :
479 [ + - - + ]: 20 : switch (self->entry_group_state)
480 : : {
481 : 4 : case _AVAHI_ENTRY_GROUP_UNCOMMITTED:
482 : 4 : _avahi_entry_group_commit (self);
483 : 4 : break;
484 : :
485 : : case _AVAHI_ENTRY_GROUP_REGISTERING:
486 : : case _AVAHI_ENTRY_GROUP_ESTABLISHED:
487 : : break;
488 : :
489 : 0 : case _AVAHI_ENTRY_GROUP_COLLISION:
490 : 0 : g_warning ("%s(): DNS-SD service name \"%s\" already registered",
491 : : G_STRFUNC, self->name);
492 : 0 : break;
493 : :
494 : 0 : case _AVAHI_ENTRY_GROUP_FAILURE:
495 : 0 : g_warning ("%s(): DNS-SD failure: %s", G_STRFUNC, error);
496 : 0 : break;
497 : : }
498 : : }
499 : :
500 : : static void
501 : 4 : _avahi_entry_group_get_state_cb (GDBusConnection *connection,
502 : : GAsyncResult *result,
503 : : ValentLanDNSSD *self)
504 : : {
505 : 4 : g_autoptr (GVariant) reply = NULL;
506 : 4 : g_autoptr (GError) error = NULL;
507 : :
508 : 4 : reply = g_dbus_connection_call_finish (connection, result, &error);
509 [ - + ]: 4 : if (reply == NULL)
510 : : {
511 [ # # ]: 0 : if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
512 : 0 : g_warning ("%s(): %s", G_STRFUNC, error->message);
513 : :
514 [ # # ]: 0 : return;
515 : : }
516 : :
517 [ + - ]: 4 : if (self->entry_group_path != NULL)
518 : : {
519 : 4 : g_variant_get (reply, "(i)", &self->entry_group_state);
520 : 8 : self->entry_group_state_id =
521 : 4 : g_dbus_connection_signal_subscribe (self->connection,
522 : : AVAHI_DBUS_NAME,
523 : : AVAHI_ENTRY_GROUP_IFACE,
524 : : "StateChanged",
525 : 4 : self->entry_group_path,
526 : : NULL,
527 : : G_DBUS_SIGNAL_FLAGS_NONE,
528 : : _avahi_entry_group_state_changed,
529 : 4 : weak_ref_new (self),
530 : : weak_ref_free);
531 : :
532 : : /* If the initial state is "uncommitted" call `AddService()` then
533 : : * `Commit()`, since `StateChanged` won't be emitted in that case.
534 : : */
535 [ + - ]: 4 : if (self->entry_group_state == _AVAHI_ENTRY_GROUP_UNCOMMITTED)
536 : : {
537 : 4 : _avahi_entry_group_add_service (self);
538 : : }
539 : : }
540 : : }
541 : :
542 : : static void
543 : 4 : _avahi_entry_group_new_cb (GDBusConnection *connection,
544 : : GAsyncResult *result,
545 : : ValentLanDNSSD *self)
546 : : {
547 : 4 : g_autoptr (GVariant) reply = NULL;
548 : 4 : g_autoptr (GError) error = NULL;
549 : :
550 : 4 : reply = g_dbus_connection_call_finish (connection, result, &error);
551 [ - + ]: 4 : if (reply == NULL)
552 : : {
553 [ # # ]: 0 : if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
554 : 0 : g_warning ("%s(): %s", G_STRFUNC, error->message);
555 : :
556 [ # # ]: 0 : return;
557 : : }
558 : :
559 [ + - ]: 4 : if (self->entry_group_path == NULL)
560 : : {
561 : 4 : g_variant_get (reply, "(o)", &self->entry_group_path);
562 : 4 : g_dbus_connection_call (self->connection,
563 : : AVAHI_DBUS_NAME,
564 : 4 : self->entry_group_path,
565 : : AVAHI_ENTRY_GROUP_IFACE,
566 : : "GetState",
567 : : NULL,
568 : : G_VARIANT_TYPE ("(i)"),
569 : : G_DBUS_CALL_FLAGS_NO_AUTO_START,
570 : : -1,
571 : : self->cancellable,
572 : : (GAsyncReadyCallback)_avahi_entry_group_get_state_cb,
573 : : self);
574 : : }
575 : : }
576 : :
577 : : static void
578 : 4 : _avahi_entry_group_new (ValentLanDNSSD *self)
579 : : {
580 [ - + ]: 4 : g_assert (VALENT_IS_LAN_DNSSD (self));
581 : :
582 [ + - ]: 4 : if (self->entry_group_path == NULL)
583 : : {
584 : 4 : g_dbus_connection_call (self->connection,
585 : : AVAHI_DBUS_NAME,
586 : : AVAHI_SERVER2_PATH,
587 : : AVAHI_SERVER2_IFACE,
588 : : "EntryGroupNew",
589 : : NULL,
590 : : G_VARIANT_TYPE ("(o)"),
591 : : G_DBUS_CALL_FLAGS_NO_AUTO_START,
592 : : -1,
593 : : self->cancellable,
594 : : (GAsyncReadyCallback)_avahi_entry_group_new_cb,
595 : : self);
596 : : }
597 : 4 : }
598 : :
599 : : static void
600 : 4 : _avahi_entry_group_add_service_cb (GDBusConnection *connection,
601 : : GAsyncResult *result,
602 : : ValentLanDNSSD *self)
603 : : {
604 : 4 : g_autoptr (GVariant) reply = NULL;
605 : 4 : g_autoptr (GError) error = NULL;
606 : :
607 : 4 : reply = g_dbus_connection_call_finish (connection, result, &error);
608 : :
609 [ - + ]: 4 : if (reply == NULL)
610 : : {
611 [ # # ]: 0 : if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
612 : 0 : g_warning ("%s(): %s", G_STRFUNC, error->message);
613 : :
614 [ # # ]: 0 : return;
615 : : }
616 : :
617 [ - + ]: 4 : _avahi_entry_group_commit (self);
618 : : }
619 : :
620 : : static void
621 : 9 : _avahi_entry_group_add_service (ValentLanDNSSD *self)
622 : : {
623 [ + - + + ]: 9 : if (self->identity == NULL || self->entry_group_path == NULL)
624 : : return;
625 : :
626 [ + + ]: 5 : if (self->entry_group_state == _AVAHI_ENTRY_GROUP_UNCOMMITTED)
627 : : {
628 : 8 : g_dbus_connection_call (self->connection,
629 : : AVAHI_DBUS_NAME,
630 : 4 : self->entry_group_path,
631 : : AVAHI_ENTRY_GROUP_IFACE,
632 : : "AddService",
633 : : g_variant_new ("(iiussssq@aay)",
634 : : -1, // interface: AVAHI_IF_UNSPEC
635 : : -1, // protocol: AVAHI_PROTO_UNSPEC
636 : : 64, // flags: AVAHI_PUBLISH_UPDATE
637 : : self->name,
638 : : self->service_type,
639 : : "", // domain
640 : : "", // host
641 : 4 : self->port,
642 : : self->txt),
643 : : NULL,
644 : : G_DBUS_CALL_FLAGS_NO_AUTO_START,
645 : : -1,
646 : : self->cancellable,
647 : : (GAsyncReadyCallback)_avahi_entry_group_add_service_cb,
648 : : self);
649 : : }
650 [ + - ]: 1 : else if (self->entry_group_state == _AVAHI_ENTRY_GROUP_REGISTERING ||
651 : : self->entry_group_state == _AVAHI_ENTRY_GROUP_ESTABLISHED)
652 : : {
653 : 1 : g_dbus_connection_call (self->connection,
654 : : AVAHI_DBUS_NAME,
655 : 1 : self->entry_group_path,
656 : : AVAHI_ENTRY_GROUP_IFACE,
657 : : "UpdateServiceTxt",
658 : : g_variant_new ("(iiusss@aay)",
659 : : -1, // interface: AVAHI_IF_UNSPEC
660 : : -1, // protocol: AVAHI_PROTO_UNSPEC
661 : : 0, // flags: AvahiPublishFlags
662 : : self->name,
663 : : self->service_type,
664 : : "", // domain
665 : : self->txt),
666 : : NULL,
667 : : G_DBUS_CALL_FLAGS_NO_AUTO_START,
668 : : -1,
669 : : self->cancellable,
670 : : NULL,
671 : : NULL);
672 : : }
673 : : }
674 : :
675 : : static void
676 : 8 : _avahi_entry_group_commit (ValentLanDNSSD *self)
677 : : {
678 [ + - ]: 8 : if (self->entry_group_path != NULL &&
679 [ + - ]: 8 : self->entry_group_state == _AVAHI_ENTRY_GROUP_UNCOMMITTED)
680 : : {
681 : 8 : g_dbus_connection_call (self->connection,
682 : : AVAHI_DBUS_NAME,
683 : : self->entry_group_path,
684 : : AVAHI_ENTRY_GROUP_IFACE,
685 : : "Commit",
686 : : NULL,
687 : : NULL,
688 : : G_DBUS_CALL_FLAGS_NO_AUTO_START,
689 : : -1,
690 : : self->cancellable,
691 : : NULL,
692 : : NULL);
693 : : }
694 : 8 : }
695 : :
696 : : static void
697 : 4 : _avahi_entry_group_reset (ValentLanDNSSD *self)
698 : : {
699 [ + + ]: 4 : if (self->entry_group_path != NULL &&
700 [ + - ]: 1 : (self->entry_group_state == _AVAHI_ENTRY_GROUP_REGISTERING ||
701 : : self->entry_group_state == _AVAHI_ENTRY_GROUP_ESTABLISHED))
702 : : {
703 : 1 : g_dbus_connection_call (self->connection,
704 : : AVAHI_DBUS_NAME,
705 : : self->entry_group_path,
706 : : AVAHI_ENTRY_GROUP_IFACE,
707 : : "Reset",
708 : : NULL,
709 : : NULL,
710 : : G_DBUS_CALL_FLAGS_NO_AUTO_START,
711 : : -1,
712 : : self->cancellable,
713 : : NULL,
714 : : NULL);
715 : : }
716 : 4 : }
717 : :
718 : : /*
719 : : * Service Browser
720 : : *
721 : : * These functions aggregate DNS-SD services into [iface@Gio.SocketConnectable]
722 : : * objects for the [iface@Gio.ListModel] implementation.
723 : : */
724 : : static inline gboolean
725 : 1 : find_service_func (gconstpointer a,
726 : : gconstpointer b)
727 : : {
728 : 1 : ValentAvahiConnectable *connectable = (ValentAvahiConnectable *)a;
729 : :
730 : 1 : return g_strcmp0 (connectable->service_name, (const char *)b) == 0;
731 : : }
732 : :
733 : : static gboolean
734 : 3 : pending_cb (gpointer data)
735 : : {
736 : 3 : ValentLanDNSSD *self = VALENT_LAN_DNSSD (data);
737 : 3 : GHashTableIter iter;
738 : 3 : const char *name;
739 : 3 : ValentAvahiConnectable *connectable;
740 : 3 : unsigned int position = 0;
741 : 3 : unsigned int added = 0;
742 : :
743 : 3 : position = self->items->len;
744 : :
745 : 3 : g_hash_table_iter_init (&iter, self->pending);
746 [ + + ]: 6 : while (g_hash_table_iter_next (&iter, (void **)&name, (void **)&connectable))
747 : : {
748 : 3 : unsigned int pos = 0;
749 : :
750 [ - + ]: 3 : if (g_ptr_array_find_with_equal_func (self->items,
751 : : name,
752 : : find_service_func,
753 : : &pos))
754 : : {
755 : 0 : ValentAvahiConnectable *current = g_ptr_array_index (self->items, pos);
756 : 0 : g_ptr_array_extend_and_steal (current->items,
757 : 0 : g_steal_pointer (&connectable->items));
758 : : }
759 : : else
760 : : {
761 : 3 : g_ptr_array_add (self->items, g_object_ref (connectable));
762 : 3 : added += 1;
763 : : }
764 : :
765 : 3 : g_hash_table_iter_remove (&iter);
766 : : }
767 : :
768 [ + - ]: 3 : if (added > 0)
769 : 3 : g_list_model_items_changed (G_LIST_MODEL (self), position, 0, added);
770 : :
771 : 3 : self->pending_id = 0;
772 : 3 : return G_SOURCE_REMOVE;
773 : : }
774 : :
775 : : static void
776 : 12 : _avahi_service_browser_event (GDBusConnection *connection,
777 : : const char *sender_name,
778 : : const char *object_path,
779 : : const char *interface_name,
780 : : const char *signal_name,
781 : : GVariant *parameters,
782 : : gpointer user_data)
783 : : {
784 : 24 : g_autoptr (ValentLanDNSSD) self = g_weak_ref_get ((GWeakRef *)user_data);
785 : 12 : int interface = 0;
786 : 12 : int protocol = 0;
787 : 12 : const char *name = 0;
788 : 12 : const char *type = 0;
789 : 12 : const char *domain = 0;
790 : 12 : uint32_t flags = 0;
791 : :
792 [ + - - + ]: 12 : g_assert (self == NULL || VALENT_IS_LAN_DNSSD (self));
793 [ + - ]: 12 : g_assert (signal_name != NULL && parameters != NULL);
794 : :
795 [ + - + - ]: 12 : if (self == NULL || valent_object_in_destruction (VALENT_OBJECT (self)))
796 : : return;
797 : :
798 : : /* Ignoring "CacheExhausted", "AllForNow" */
799 : 12 : VALENT_NOTE ("%s", signal_name);
800 : :
801 [ + + ]: 12 : if (g_str_equal (signal_name, "ItemNew"))
802 : : {
803 : 3 : ValentAvahiConnectable *connectable = NULL;
804 : :
805 : 3 : g_variant_get (parameters,
806 : : "(ii&s&s&su)",
807 : : &interface,
808 : : &protocol,
809 : : &name,
810 : : &type,
811 : : &domain,
812 : : &flags);
813 : :
814 : : /* Ignore announcements with an invalid service name (i.e. device ID)
815 : : */
816 [ - + ]: 3 : if (!valent_device_validate_id (name))
817 : : {
818 : 0 : g_warning ("%s(): invalid device ID \"%s\"", G_STRFUNC, name);
819 : 0 : return;
820 : : }
821 : :
822 : 3 : connectable = g_hash_table_lookup (self->pending, name);
823 [ + - ]: 3 : if (connectable == NULL)
824 : : {
825 : 3 : connectable = g_object_new (VALENT_TYPE_AVAHI_CONNECTABLE, NULL);
826 : 3 : connectable->connection = g_object_ref (connection);
827 [ - + ]: 3 : connectable->service_name = g_strdup (name);
828 [ - + ]: 6 : g_hash_table_replace (self->pending, g_strdup (name), connectable);
829 : :
830 [ - + ]: 3 : if (self->pending_id == 0)
831 : 3 : self->pending_id = g_idle_add (pending_cb, self);
832 : : }
833 : :
834 : 3 : g_ptr_array_add (connectable->items, g_variant_ref_sink (parameters));
835 : : }
836 [ + + ]: 9 : else if (g_str_equal (signal_name, "ItemRemove"))
837 : : {
838 : 1 : unsigned int position = 0;
839 : :
840 : 1 : g_variant_get (parameters,
841 : : "(ii&s&s&su)",
842 : : &interface,
843 : : &protocol,
844 : : &name,
845 : : &type,
846 : : &domain,
847 : : &flags);
848 : :
849 [ + - ]: 1 : if (g_ptr_array_find_with_equal_func (self->items,
850 : : name,
851 : : find_service_func,
852 : : &position))
853 : : {
854 : 1 : ValentAvahiConnectable *connectable = NULL;
855 : :
856 : 1 : connectable = g_ptr_array_index (self->items, position);
857 [ + - ]: 1 : for (unsigned int i = 0; i < connectable->items->len; i++)
858 : : {
859 : 1 : GVariant *params = g_ptr_array_index (connectable->items, i);
860 : 1 : int32_t interface_ = 0;
861 : 1 : int32_t protocol_ = 0;
862 : 1 : const char *name_ = 0;
863 : 1 : const char *type_ = 0;
864 : 1 : const char *domain_ = 0;
865 : 1 : uint32_t flags_ = 0;
866 : :
867 : 1 : g_variant_get (params,
868 : : "(ii&s&s&su)",
869 : : &interface_,
870 : : &protocol_,
871 : : &name_,
872 : : &type_,
873 : : &domain_,
874 : : &flags_);
875 : :
876 [ + - ]: 1 : if (interface == interface_ &&
877 [ + - ]: 1 : protocol == protocol_ &&
878 [ + - ]: 1 : g_str_equal (domain, domain_))
879 : : {
880 : 1 : g_ptr_array_remove_index (connectable->items, i);
881 : 1 : break;
882 : : }
883 : : }
884 : :
885 [ + - ]: 1 : if (connectable->items->len == 0)
886 : : {
887 : 1 : g_ptr_array_remove_index (self->items, position);
888 : 1 : g_list_model_items_changed (G_LIST_MODEL (self), position, 1, 0);
889 : : }
890 : : }
891 : : }
892 [ - + ]: 8 : else if (g_str_equal (signal_name, "Failure"))
893 : : {
894 : 0 : const char *error = NULL;
895 : :
896 : 0 : g_variant_get (parameters, "&s", &error);
897 : 0 : g_warning ("%s(): %s", G_STRFUNC, error);
898 : : }
899 : : }
900 : :
901 : : static void
902 : 4 : _avahi_service_browser_start_cb (GDBusConnection *connection,
903 : : GAsyncResult *result,
904 : : ValentLanDNSSD *self)
905 : : {
906 : 8 : g_autoptr (GVariant) reply = NULL;
907 [ + - ]: 4 : g_autoptr (GError) error = NULL;
908 : :
909 : 4 : reply = g_dbus_connection_call_finish (connection, result, &error);
910 [ - + ]: 4 : if (reply == NULL)
911 : : {
912 [ # # ]: 0 : if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
913 : 0 : g_warning ("%s(): %s", G_STRFUNC, error->message);
914 : :
915 [ # # ]: 0 : if (self->service_browser_event_id != 0)
916 : : {
917 : 0 : g_dbus_connection_signal_unsubscribe (connection,
918 : : self->service_browser_event_id);
919 : 0 : self->service_browser_event_id = 0;
920 : : }
921 : :
922 [ - - - + ]: 4 : g_clear_pointer (&self->service_browser_path, g_free);
923 : : }
924 : 4 : }
925 : :
926 : : static void
927 : 4 : _avahi_service_browser_prepare_cb (GDBusConnection *connection,
928 : : GAsyncResult *result,
929 : : ValentLanDNSSD *self)
930 : : {
931 : 4 : g_autoptr (GVariant) reply = NULL;
932 : 4 : g_autoptr (GError) error = NULL;
933 : :
934 : 4 : reply = g_dbus_connection_call_finish (connection, result, &error);
935 [ - + ]: 4 : if (reply == NULL)
936 : : {
937 [ # # ]: 0 : if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
938 : 0 : g_warning ("%s(): %s", G_STRFUNC, error->message);
939 : :
940 [ # # ]: 0 : return;
941 : : }
942 : :
943 : 4 : g_variant_get (reply, "(o)", &self->service_browser_path);
944 : 8 : self->service_browser_event_id =
945 : 4 : g_dbus_connection_signal_subscribe (self->connection,
946 : : AVAHI_DBUS_NAME,
947 : : AVAHI_SERVICE_BROWSER_IFACE,
948 : : NULL, // all signals
949 : 4 : self->service_browser_path,
950 : : NULL,
951 : : G_DBUS_SIGNAL_FLAGS_NONE,
952 : : _avahi_service_browser_event,
953 : 4 : weak_ref_new (self),
954 : : weak_ref_free);
955 : :
956 [ - + ]: 4 : g_dbus_connection_call (self->connection,
957 : : AVAHI_DBUS_NAME,
958 : 4 : self->service_browser_path,
959 : : AVAHI_SERVICE_BROWSER_IFACE,
960 : : "Start",
961 : : NULL,
962 : : NULL,
963 : : G_DBUS_CALL_FLAGS_NO_AUTO_START,
964 : : -1,
965 : : self->cancellable,
966 : : (GAsyncReadyCallback)_avahi_service_browser_start_cb,
967 : : self);
968 : : }
969 : :
970 : : static void
971 : 4 : _avahi_service_browser_prepare (ValentLanDNSSD *self)
972 : : {
973 [ - + ]: 4 : g_assert (VALENT_IS_LAN_DNSSD (self));
974 : :
975 [ + - ]: 4 : if (self->service_browser_path == NULL)
976 : : {
977 : 4 : g_dbus_connection_call (self->connection,
978 : : AVAHI_DBUS_NAME,
979 : : AVAHI_SERVER2_PATH,
980 : : AVAHI_SERVER2_IFACE,
981 : : "ServiceBrowserPrepare",
982 : : g_variant_new ("(iissu)",
983 : : -1, // interface: AVAHI_IF_UNSPEC
984 : : -1, // protocol: AVAHI_PROTO_UNSPEC
985 : : self->service_type,
986 : : "", // domain
987 : : 0), // flags: AvahiLookupFlags
988 : : G_VARIANT_TYPE ("(o)"),
989 : : G_DBUS_CALL_FLAGS_NO_AUTO_START,
990 : : -1,
991 : : self->cancellable,
992 : : (GAsyncReadyCallback)_avahi_service_browser_prepare_cb,
993 : : self);
994 : : }
995 : 4 : }
996 : :
997 : : static void
998 : 0 : _avahi_server_state_changed (GDBusConnection *connection,
999 : : const char *sender_name,
1000 : : const char *object_path,
1001 : : const char *interface_name,
1002 : : const char *signal_name,
1003 : : GVariant *parameters,
1004 : : gpointer user_data)
1005 : : {
1006 : 0 : g_autoptr (ValentLanDNSSD) self = g_weak_ref_get ((GWeakRef *)user_data);
1007 : 0 : const char *error = NULL;
1008 : :
1009 [ # # # # ]: 0 : g_assert (self == NULL || VALENT_IS_LAN_DNSSD (self));
1010 [ # # ]: 0 : g_assert (g_str_equal (signal_name, "StateChanged"));
1011 : :
1012 [ # # # # ]: 0 : if (self == NULL || valent_object_in_destruction (VALENT_OBJECT (self)))
1013 : 0 : return;
1014 : :
1015 : 0 : g_variant_get (parameters, "(i&s)", &self->server_state, &error);
1016 : :
1017 : 0 : VALENT_NOTE ("[%i] %s", self->server_state, error);
1018 : :
1019 [ # # # # ]: 0 : switch (self->server_state)
1020 : : {
1021 : : case _AVAHI_SERVER_INVALID:
1022 : : case _AVAHI_SERVER_REGISTERING:
1023 : : break;
1024 : :
1025 : 0 : case _AVAHI_SERVER_RUNNING:
1026 : 0 : _avahi_entry_group_new (self);
1027 : 0 : _avahi_service_browser_prepare (self);
1028 : 0 : break;
1029 : :
1030 : 0 : case _AVAHI_SERVER_COLLISION:
1031 : 0 : g_warning ("%s(): DNS-SD server collision: %s", G_STRFUNC, error);
1032 : 0 : break;
1033 : :
1034 : 0 : case _AVAHI_SERVER_FAILURE:
1035 : 0 : g_warning ("%s(): DNS-SD server failure: %s", G_STRFUNC, error);
1036 : 0 : break;
1037 : : }
1038 : : }
1039 : :
1040 : : static void
1041 : 4 : _avahi_server_get_state_cb (GDBusConnection *connection,
1042 : : GAsyncResult *result,
1043 : : ValentLanDNSSD *self)
1044 : : {
1045 : 4 : g_autoptr (GVariant) reply = NULL;
1046 : 4 : g_autoptr (GCancellable) destroy = NULL;
1047 : 4 : g_autoptr (GError) error = NULL;
1048 : :
1049 : 4 : reply = g_dbus_connection_call_finish (connection, result, &error);
1050 [ - + ]: 4 : if (reply == NULL)
1051 : : {
1052 [ # # ]: 0 : if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
1053 : 0 : g_warning ("%s(): %s", G_STRFUNC, error->message);
1054 : :
1055 [ # # ]: 0 : return;
1056 : : }
1057 : :
1058 : 4 : g_variant_get (reply, "(i)", &self->server_state);
1059 : 8 : self->server_state_id =
1060 : 4 : g_dbus_connection_signal_subscribe (self->connection,
1061 : : AVAHI_DBUS_NAME,
1062 : : AVAHI_SERVER2_IFACE,
1063 : : "StateChanged",
1064 : : AVAHI_SERVER2_PATH,
1065 : : NULL,
1066 : : G_DBUS_SIGNAL_FLAGS_NONE,
1067 : : _avahi_server_state_changed,
1068 : 4 : weak_ref_new (self),
1069 : : weak_ref_free);
1070 : :
1071 : : /* If the initial state is "running" call `EntryGroupNew()` and
1072 : : * `ServiceBrowserPrepare()`, otherwise wait for a `StateChanged` emission.
1073 : : */
1074 [ + - ]: 4 : if (self->server_state == _AVAHI_SERVER_RUNNING)
1075 : : {
1076 : 4 : _avahi_entry_group_new (self);
1077 : 4 : _avahi_service_browser_prepare (self);
1078 : : }
1079 : : }
1080 : :
1081 : : static void
1082 : 4 : on_name_appeared (GDBusConnection *connection,
1083 : : const char *name,
1084 : : const char *name_owner,
1085 : : ValentLanDNSSD *self)
1086 : : {
1087 : 8 : g_autoptr (GCancellable) destroy = NULL;
1088 : 8 : g_autoptr (GVariant) reply = NULL;
1089 : 8 : g_autoptr (GError) error = NULL;
1090 : :
1091 [ - + ]: 4 : g_assert (VALENT_IS_LAN_DNSSD (self));
1092 : :
1093 : : /* Create a new cancellable, chained to the object's cancellable, so that
1094 : : * any operations will be cancelled if the object is destroyed.
1095 : : */
1096 : 4 : self->connection = g_object_ref (connection);
1097 : 4 : self->cancellable = g_cancellable_new ();
1098 : 4 : g_signal_connect_data (self,
1099 : : "destroy",
1100 : : G_CALLBACK (g_cancellable_cancel),
1101 : : g_object_ref (self->cancellable),
1102 : : (GClosureNotify)(void (*) (void))g_object_unref,
1103 : : G_CONNECT_SWAPPED);
1104 : 4 : g_dbus_connection_call (self->connection,
1105 : : AVAHI_DBUS_NAME,
1106 : : AVAHI_SERVER2_PATH,
1107 : : AVAHI_SERVER2_IFACE,
1108 : : "GetState",
1109 : : NULL,
1110 : : G_VARIANT_TYPE ("(i)"),
1111 : : G_DBUS_CALL_FLAGS_NO_AUTO_START,
1112 : : -1,
1113 : : self->cancellable,
1114 : : (GAsyncReadyCallback)_avahi_server_get_state_cb,
1115 : : self);
1116 : 4 : }
1117 : :
1118 : : static void
1119 : 4 : on_name_vanished (GDBusConnection *connection,
1120 : : const char *name,
1121 : : ValentLanDNSSD *self)
1122 : : {
1123 [ - + ]: 4 : g_assert (VALENT_IS_LAN_DNSSD (self));
1124 : :
1125 [ - + ]: 4 : if (self->cancellable != NULL)
1126 : : {
1127 : 0 : g_signal_handlers_disconnect_by_data (self, self->cancellable);
1128 : 0 : g_cancellable_cancel (self->cancellable);
1129 [ # # ]: 0 : g_clear_object (&self->cancellable);
1130 : : }
1131 : :
1132 [ + - ]: 4 : if (self->connection != NULL)
1133 : : {
1134 [ + - ]: 4 : if (self->server_state_id != 0)
1135 : : {
1136 : 4 : g_dbus_connection_signal_unsubscribe (self->connection,
1137 : : self->server_state_id);
1138 : 4 : self->server_state_id = 0;
1139 : : }
1140 : :
1141 [ + - ]: 4 : if (self->entry_group_state_id != 0)
1142 : : {
1143 : 4 : g_dbus_connection_signal_unsubscribe (self->connection,
1144 : : self->entry_group_state_id);
1145 : 4 : self->entry_group_state_id = 0;
1146 : : }
1147 : :
1148 [ + - ]: 4 : if (self->service_browser_event_id != 0)
1149 : : {
1150 : 4 : g_dbus_connection_signal_unsubscribe (self->connection,
1151 : : self->service_browser_event_id);
1152 : 4 : self->service_browser_event_id = 0;
1153 : : }
1154 : :
1155 : 4 : self->entry_group_state = _AVAHI_ENTRY_GROUP_UNCOMMITTED;
1156 : 4 : self->server_state = _AVAHI_SERVER_INVALID;
1157 : :
1158 [ - + ]: 4 : g_clear_handle_id (&self->pending_id, g_source_remove);
1159 [ + - ]: 4 : g_clear_pointer (&self->service_browser_path, g_free);
1160 [ + - ]: 4 : g_clear_pointer (&self->entry_group_path, g_free);
1161 [ + - ]: 4 : g_clear_object (&self->connection);
1162 : : }
1163 : 4 : }
1164 : :
1165 : : static void
1166 : 4 : _avahi_client_connect (ValentLanDNSSD *self)
1167 : : {
1168 [ + - ]: 4 : if (self->watcher_id == 0)
1169 : : {
1170 : 4 : self->watcher_id = g_bus_watch_name (G_BUS_TYPE_SYSTEM,
1171 : : AVAHI_DBUS_NAME,
1172 : : G_BUS_NAME_WATCHER_FLAGS_NONE,
1173 : : (GBusNameAppearedCallback)on_name_appeared,
1174 : : (GBusNameVanishedCallback)on_name_vanished,
1175 : : self, NULL);
1176 : : }
1177 : 4 : }
1178 : :
1179 : : static void
1180 : 4 : _avahi_client_disconnect (ValentLanDNSSD *self)
1181 : : {
1182 [ + - ]: 4 : if (self->cancellable != NULL)
1183 : : {
1184 : 4 : g_signal_handlers_disconnect_by_data (self, self->cancellable);
1185 : 4 : g_cancellable_cancel (self->cancellable);
1186 [ + - ]: 4 : g_clear_object (&self->cancellable);
1187 : : }
1188 : :
1189 [ + - ]: 4 : if (self->connection != NULL)
1190 : : {
1191 [ + - ]: 4 : if (self->entry_group_path != NULL)
1192 : : {
1193 : 4 : g_dbus_connection_call (self->connection,
1194 : : AVAHI_DBUS_NAME,
1195 : : self->entry_group_path,
1196 : : AVAHI_ENTRY_GROUP_IFACE,
1197 : : "Free",
1198 : : NULL,
1199 : : NULL,
1200 : : G_DBUS_CALL_FLAGS_NO_AUTO_START,
1201 : : -1,
1202 : : NULL,
1203 : : NULL,
1204 : : NULL);
1205 : : }
1206 : :
1207 [ + - ]: 4 : if (self->service_browser_path != NULL)
1208 : : {
1209 : 4 : g_dbus_connection_call (self->connection,
1210 : : AVAHI_DBUS_NAME,
1211 : : self->service_browser_path,
1212 : : AVAHI_SERVICE_BROWSER_IFACE,
1213 : : "Free",
1214 : : NULL,
1215 : : NULL,
1216 : : G_DBUS_CALL_FLAGS_NO_AUTO_START,
1217 : : -1,
1218 : : NULL,
1219 : : NULL,
1220 : : NULL);
1221 : : }
1222 : :
1223 [ + - ]: 4 : g_clear_handle_id (&self->watcher_id, g_bus_unwatch_name);
1224 : 4 : on_name_vanished (self->connection, AVAHI_DBUS_NAME, self);
1225 : : }
1226 : 4 : }
1227 : :
1228 : : /*
1229 : : * ValentLanDNSSD
1230 : : */
1231 : : static void
1232 : 9 : valent_lan_dnssd_set_identity (ValentLanDNSSD *self,
1233 : : JsonNode *identity)
1234 : : {
1235 : 9 : g_autoptr (GPtrArray) txt = NULL;
1236 : 9 : const char *id = NULL;
1237 : 9 : const char *name = NULL;
1238 : 9 : const char *type = NULL;
1239 : 9 : gint64 protocol = 0;
1240 : 9 : gint64 port = VALENT_LAN_PROTOCOL_PORT;
1241 : :
1242 [ - + ]: 9 : g_assert (VALENT_IS_LAN_DNSSD (self));
1243 [ + + + - ]: 9 : g_assert (identity == NULL || VALENT_IS_PACKET (identity));
1244 : :
1245 [ + + ]: 9 : if (identity == NULL)
1246 : : {
1247 [ + + ]: 4 : g_clear_pointer (&self->identity, json_node_unref);
1248 : 4 : _avahi_entry_group_reset (self);
1249 : 4 : return;
1250 : : }
1251 : :
1252 : : /* Even if the pointers match, assume the contents have changed */
1253 [ + + ]: 5 : if (self->identity != identity)
1254 : : {
1255 [ - + ]: 4 : g_clear_pointer (&self->identity, json_node_unref);
1256 : 4 : self->identity = json_node_ref (identity);
1257 : : }
1258 : :
1259 : : /* Service TXT Record */
1260 : 5 : txt = g_ptr_array_new ();
1261 : :
1262 [ + - ]: 5 : if (valent_packet_get_string (identity, "deviceId", &id))
1263 : 5 : g_ptr_array_add (txt, txt_new_str ("id", id));
1264 : :
1265 [ + - ]: 5 : if (valent_packet_get_string (identity, "deviceName", &name))
1266 : 5 : g_ptr_array_add (txt, txt_new_str ("name", name));
1267 : :
1268 [ + - ]: 5 : if (valent_packet_get_string (identity, "deviceType", &type))
1269 : 5 : g_ptr_array_add (txt, txt_new_str ("type", type));
1270 : :
1271 [ + - ]: 5 : if (valent_packet_get_int (identity, "protocolVersion", &protocol))
1272 : 5 : g_ptr_array_add (txt, txt_new_uint ("protocol", (uint32_t)protocol));
1273 : :
1274 [ + + ]: 5 : g_clear_pointer (&self->txt, g_variant_unref);
1275 : 10 : self->txt = g_variant_new_array (G_VARIANT_TYPE_BYTESTRING,
1276 : 5 : (GVariant * const *)txt->pdata,
1277 : 5 : txt->len);
1278 : 5 : g_variant_ref_sink (self->txt);
1279 : :
1280 : : /* Service Name and Port
1281 : : */
1282 : 5 : g_set_str (&self->name, id);
1283 : :
1284 [ + - ]: 5 : if (valent_packet_get_int (identity, "tcpPort", &port))
1285 : 5 : self->port = (uint16_t)port;
1286 : :
1287 : 5 : _avahi_entry_group_add_service (self);
1288 : : }
1289 : :
1290 : : /*
1291 : : * ValentObject
1292 : : */
1293 : : static void
1294 : 4 : valent_lan_dnssd_destroy (ValentObject *object)
1295 : : {
1296 : 4 : ValentLanDNSSD *self = VALENT_LAN_DNSSD (object);
1297 : :
1298 : 4 : valent_lan_dnssd_stop (self);
1299 : :
1300 : 4 : VALENT_OBJECT_CLASS (valent_lan_dnssd_parent_class)->destroy (object);
1301 : 4 : }
1302 : :
1303 : : /*
1304 : : * GObject
1305 : : */
1306 : : static void
1307 : 4 : valent_lan_dnssd_finalize (GObject *object)
1308 : : {
1309 : 4 : ValentLanDNSSD *self = VALENT_LAN_DNSSD (object);
1310 : :
1311 [ + - ]: 4 : g_clear_pointer (&self->name, g_free);
1312 [ + - ]: 4 : g_clear_pointer (&self->txt, g_variant_unref);
1313 [ + + ]: 4 : g_clear_pointer (&self->identity, json_node_unref);
1314 [ + - ]: 4 : g_clear_pointer (&self->service_type, g_free);
1315 [ + - ]: 4 : g_clear_pointer (&self->items, g_ptr_array_unref);
1316 [ + - ]: 4 : g_clear_pointer (&self->pending, g_hash_table_unref);
1317 [ - + ]: 4 : g_clear_handle_id (&self->pending_id, g_source_remove);
1318 : :
1319 : 4 : G_OBJECT_CLASS (valent_lan_dnssd_parent_class)->finalize (object);
1320 : 4 : }
1321 : :
1322 : : static void
1323 : 2 : valent_lan_dnssd_get_property (GObject *object,
1324 : : guint prop_id,
1325 : : GValue *value,
1326 : : GParamSpec *pspec)
1327 : : {
1328 : 2 : ValentLanDNSSD *self = VALENT_LAN_DNSSD (object);
1329 : :
1330 [ + + - ]: 2 : switch ((ValentLanDNSSDProperty)prop_id)
1331 : : {
1332 : 1 : case PROP_IDENTITY:
1333 : 1 : g_value_set_boxed (value, self->identity);
1334 : 1 : break;
1335 : :
1336 : 1 : case PROP_SERVICE_TYPE:
1337 : 1 : g_value_set_string (value, self->service_type);
1338 : 1 : break;
1339 : :
1340 : 0 : default:
1341 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1342 : : }
1343 : 2 : }
1344 : :
1345 : : static void
1346 : 13 : valent_lan_dnssd_set_property (GObject *object,
1347 : : guint prop_id,
1348 : : const GValue *value,
1349 : : GParamSpec *pspec)
1350 : : {
1351 : 13 : ValentLanDNSSD *self = VALENT_LAN_DNSSD (object);
1352 : :
1353 [ + + - ]: 13 : switch ((ValentLanDNSSDProperty)prop_id)
1354 : : {
1355 : 9 : case PROP_IDENTITY:
1356 : 9 : valent_lan_dnssd_set_identity (self, g_value_get_boxed (value));
1357 : 9 : break;
1358 : :
1359 : 4 : case PROP_SERVICE_TYPE:
1360 : 4 : self->service_type = g_value_dup_string (value);
1361 : 4 : break;
1362 : :
1363 : 0 : default:
1364 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1365 : : }
1366 : 13 : }
1367 : :
1368 : : static void
1369 : 2 : valent_lan_dnssd_class_init (ValentLanDNSSDClass *klass)
1370 : : {
1371 : 2 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
1372 : 2 : ValentObjectClass *vobject_class = VALENT_OBJECT_CLASS (klass);
1373 : :
1374 : 2 : object_class->finalize = valent_lan_dnssd_finalize;
1375 : 2 : object_class->get_property = valent_lan_dnssd_get_property;
1376 : 2 : object_class->set_property = valent_lan_dnssd_set_property;
1377 : :
1378 : 2 : vobject_class->destroy = valent_lan_dnssd_destroy;
1379 : :
1380 : : /**
1381 : : * ValentLanDNSSD:identity:
1382 : : *
1383 : : * The KDE Connect packet holding the local identity.
1384 : : */
1385 : 4 : properties [PROP_IDENTITY] =
1386 : 2 : g_param_spec_boxed ("identity", NULL, NULL,
1387 : : JSON_TYPE_NODE,
1388 : : (G_PARAM_READWRITE |
1389 : : G_PARAM_EXPLICIT_NOTIFY |
1390 : : G_PARAM_STATIC_STRINGS));
1391 : :
1392 : : /**
1393 : : * ValentLanDNSSD:service-type:
1394 : : *
1395 : : * The DNS-SD service type to register and observe.
1396 : : */
1397 : 4 : properties [PROP_SERVICE_TYPE] =
1398 : 2 : g_param_spec_string ("service-type", NULL, NULL,
1399 : : KDECONNECT_UDP_SERVICE_TYPE,
1400 : : (G_PARAM_READWRITE |
1401 : : G_PARAM_CONSTRUCT_ONLY |
1402 : : G_PARAM_EXPLICIT_NOTIFY |
1403 : : G_PARAM_STATIC_STRINGS));
1404 : :
1405 : 2 : g_object_class_install_properties (object_class, G_N_ELEMENTS (properties), properties);
1406 : 2 : }
1407 : :
1408 : : static void
1409 : 4 : valent_lan_dnssd_init (ValentLanDNSSD *self)
1410 : : {
1411 : 4 : self->items = g_ptr_array_new_with_free_func (g_object_unref);
1412 : 4 : self->pending = g_hash_table_new_full (g_str_hash,
1413 : : g_str_equal,
1414 : : g_free,
1415 : : g_object_unref);
1416 : 4 : }
1417 : :
1418 : : /**
1419 : : * valent_lan_dnssd_new:
1420 : : * @identity: (nullable): a KDE Connect identity packet
1421 : : *
1422 : : * Create a DNS-SD adapter for @identity.
1423 : : *
1424 : : * Returns: (transfer full): a `GListModel`
1425 : : */
1426 : : GListModel *
1427 : 3 : valent_lan_dnssd_new (JsonNode *identity)
1428 : : {
1429 [ - + - - ]: 3 : g_return_val_if_fail (identity == NULL || VALENT_IS_PACKET (identity), NULL);
1430 : :
1431 : 3 : return g_object_new (VALENT_TYPE_LAN_DNSSD,
1432 : : "identity", identity,
1433 : : NULL);
1434 : : }
1435 : :
1436 : : /**
1437 : : * valent_lan_dnssd_start:
1438 : : * @self: an `ValentLanDNSSD`
1439 : : *
1440 : : * Start the DNS-SD adapter.
1441 : : */
1442 : : void
1443 : 4 : valent_lan_dnssd_start (ValentLanDNSSD *self)
1444 : : {
1445 [ - + ]: 4 : g_return_if_fail (VALENT_IS_LAN_DNSSD (self));
1446 : :
1447 : 4 : _avahi_client_connect (self);
1448 : : }
1449 : :
1450 : : /**
1451 : : * valent_lan_dnssd_stop:
1452 : : * @self: an `ValentLanDNSSD`
1453 : : *
1454 : : * Stop the DNS-SD adapter.
1455 : : */
1456 : : void
1457 : 4 : valent_lan_dnssd_stop (ValentLanDNSSD *self)
1458 : : {
1459 [ - + ]: 4 : g_return_if_fail (VALENT_IS_LAN_DNSSD (self));
1460 : :
1461 : 4 : _avahi_client_disconnect (self);
1462 : : }
|