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