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