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 : 0 : 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 : 9 : g_dbus_connection_call (connection,
564 : : AVAHI_DBUS_NAME,
565 : : AVAHI_SERVER2_PATH,
566 : : AVAHI_SERVER2_IFACE,
567 : : "ResolveService",
568 : : g_variant_new ("(iisssiu)",
569 : : interface,
570 : : protocol,
571 : : name,
572 : : type,
573 : : domain,
574 : : -1, // aprotocol: AVAHI_PROTO_UNSPEC
575 : : 0), // flags: AvahiLookupFlags
576 : : G_VARIANT_TYPE ("(iissssisqaayu)"),
577 : : G_DBUS_CALL_FLAGS_NO_AUTO_START,
578 : : -1,
579 : : NULL,
580 : : (GAsyncReadyCallback)_avahi_resolve_service_cb,
581 : : self);
582 : : }
583 [ + + ]: 9 : else if (g_str_equal (signal_name, "ItemRemove"))
584 : : {
585 : 1 : g_variant_get (parameters,
586 : : "(ii&s&s&su)",
587 : : &interface,
588 : : &protocol,
589 : : &name,
590 : : &type,
591 : : &domain,
592 : : &flags);
593 : :
594 [ + + ]: 2 : for (unsigned int i = 0; i < self->items->len; i++)
595 : : {
596 : 1 : GSocketAddress *saddress = NULL;
597 : 1 : GSocketFamily sprotocol = G_SOCKET_FAMILY_INVALID;
598 : 1 : const char *device_id = NULL;
599 : :
600 : 1 : saddress = g_ptr_array_index (self->items, i);
601 : 1 : sprotocol = g_socket_address_get_family (saddress);
602 : :
603 : : /* NOTE: IPv4 = 0, IPv6 = 1, Any = -1 */
604 [ - + ]: 1 : if (protocol != -1)
605 : : {
606 [ # # # # : 0 : if ((protocol == 1 && sprotocol != G_SOCKET_FAMILY_IPV6) ||
# # ]
607 [ # # ]: 0 : (protocol == 0 && sprotocol != G_SOCKET_FAMILY_IPV4))
608 : 0 : continue;
609 : : }
610 : :
611 : 1 : device_id = _g_socket_address_get_dnssd_name (saddress);
612 : :
613 [ - + ]: 1 : if (!g_str_equal (device_id, name))
614 : 0 : continue;
615 : :
616 : 1 : valent_object_lock (VALENT_OBJECT (self));
617 : 1 : g_ptr_array_remove_index (self->items, i);
618 : 1 : valent_object_unlock (VALENT_OBJECT (self));
619 : :
620 : 1 : g_list_model_items_changed (G_LIST_MODEL (self), i, 1, 0);
621 : : }
622 : : }
623 [ - + ]: 8 : else if (g_str_equal (signal_name, "Failure"))
624 : : {
625 : 0 : const char *error = NULL;
626 : :
627 : 0 : g_variant_get (parameters, "&s", &error);
628 : 0 : g_warning ("%s(): %s", G_STRFUNC, error);
629 : : }
630 : : }
631 : :
632 : : static void
633 : 4 : _avahi_service_browser_start_cb (GDBusConnection *connection,
634 : : GAsyncResult *result,
635 : : ValentLanDNSSD *self)
636 : : {
637 : 8 : g_autoptr (GVariant) reply = NULL;
638 [ + - ]: 4 : g_autoptr (GError) error = NULL;
639 : :
640 : 4 : reply = g_dbus_connection_call_finish (connection, result, &error);
641 : :
642 [ - + ]: 4 : if (reply == NULL)
643 : : {
644 [ # # ]: 0 : if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
645 : 0 : g_warning ("%s(): %s", G_STRFUNC, error->message);
646 : :
647 : 0 : valent_object_lock (VALENT_OBJECT (self));
648 [ # # ]: 0 : if (self->service_browser_event_id != 0)
649 : : {
650 : 0 : g_dbus_connection_signal_unsubscribe (connection,
651 : : self->service_browser_event_id);
652 : 0 : self->service_browser_event_id = 0;
653 : : }
654 : :
655 [ # # ]: 0 : g_clear_pointer (&self->service_browser_path, g_free);
656 : 0 : valent_object_unlock (VALENT_OBJECT (self));
657 : : }
658 : 4 : }
659 : :
660 : : static gboolean
661 : 4 : _avahi_service_browser_prepare (ValentLanDNSSD *self)
662 : : {
663 [ + - ]: 4 : g_assert (VALENT_IS_LAN_DNSSD (self));
664 : :
665 : 4 : valent_object_lock (VALENT_OBJECT (self));
666 [ - + ]: 4 : if (self->service_browser_path == NULL)
667 : : {
668 : 4 : g_autoptr (GVariant) reply = NULL;
669 : 4 : g_autoptr (GError) error = NULL;
670 : :
671 : 4 : reply = g_dbus_connection_call_sync (self->connection,
672 : : AVAHI_DBUS_NAME,
673 : : AVAHI_SERVER2_PATH,
674 : : AVAHI_SERVER2_IFACE,
675 : : "ServiceBrowserPrepare",
676 : : g_variant_new ("(iissu)",
677 : : -1, // interface: AVAHI_IF_UNSPEC
678 : : -1, // protocol: AVAHI_PROTO_UNSPEC
679 : : self->type,
680 : : "", // domain
681 : : 0), // flags: AvahiLookupFlags
682 : : G_VARIANT_TYPE ("(o)"),
683 : : G_DBUS_CALL_FLAGS_NO_AUTO_START,
684 : : -1,
685 : : self->cancellable,
686 : : &error);
687 : :
688 [ - + ]: 4 : if (reply == NULL)
689 : : {
690 [ # # ]: 0 : if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
691 : 0 : g_warning ("%s(): %s", G_STRFUNC, error->message);
692 : :
693 [ # # ]: 0 : goto unlock_exit;
694 : : }
695 : :
696 : 4 : g_variant_get (reply, "(o)", &self->service_browser_path);
697 : :
698 : 8 : self->service_browser_event_id =
699 : 4 : g_dbus_connection_signal_subscribe (self->connection,
700 : : AVAHI_DBUS_NAME,
701 : : AVAHI_SERVICE_BROWSER_IFACE,
702 : : NULL, // all signals
703 : 4 : self->service_browser_path,
704 : : NULL,
705 : : G_DBUS_SIGNAL_FLAGS_NONE,
706 : : _avahi_service_browser_event,
707 : 4 : weak_ref_new (self),
708 : : weak_ref_free);
709 : :
710 [ - + ]: 4 : g_dbus_connection_call (self->connection,
711 : : AVAHI_DBUS_NAME,
712 : 4 : self->service_browser_path,
713 : : AVAHI_SERVICE_BROWSER_IFACE,
714 : : "Start",
715 : : NULL,
716 : : NULL,
717 : : G_DBUS_CALL_FLAGS_NO_AUTO_START,
718 : : -1,
719 : : self->cancellable,
720 : : (GAsyncReadyCallback)_avahi_service_browser_start_cb,
721 : : self);
722 : : }
723 : :
724 : 0 : unlock_exit:
725 : 4 : valent_object_unlock (VALENT_OBJECT (self));
726 : :
727 : 4 : return G_SOURCE_REMOVE;
728 : : }
729 : :
730 : : static void
731 : 0 : _avahi_server_state_changed (GDBusConnection *connection,
732 : : const char *sender_name,
733 : : const char *object_path,
734 : : const char *interface_name,
735 : : const char *signal_name,
736 : : GVariant *parameters,
737 : : gpointer user_data)
738 : : {
739 : 0 : g_autoptr (ValentLanDNSSD) self = g_weak_ref_get ((GWeakRef *)user_data);
740 : 0 : const char *error = NULL;
741 : :
742 [ # # # # ]: 0 : g_assert (self == NULL || VALENT_IS_LAN_DNSSD (self));
743 [ # # ]: 0 : g_assert (g_str_equal (signal_name, "StateChanged"));
744 : :
745 [ # # # # ]: 0 : if (self == NULL || valent_object_in_destruction (VALENT_OBJECT (self)))
746 : 0 : return;
747 : :
748 : 0 : valent_object_lock (VALENT_OBJECT (self));
749 : 0 : g_variant_get (parameters, "(i&s)", &self->server_state, &error);
750 : :
751 : 0 : VALENT_NOTE ("[%i] %s", self->server_state, error);
752 : :
753 [ # # # # ]: 0 : switch (self->server_state)
754 : : {
755 : : case _AVAHI_SERVER_INVALID:
756 : : case _AVAHI_SERVER_REGISTERING:
757 : : break;
758 : :
759 : 0 : case _AVAHI_SERVER_RUNNING:
760 : 0 : g_main_context_invoke_full (self->context,
761 : : G_PRIORITY_DEFAULT,
762 : : G_SOURCE_FUNC (_avahi_entry_group_new),
763 : : g_object_ref (self),
764 : : g_object_unref);
765 : 0 : g_main_context_invoke_full (self->context,
766 : : G_PRIORITY_DEFAULT,
767 : : G_SOURCE_FUNC (_avahi_service_browser_prepare),
768 : : g_object_ref (self),
769 : : g_object_unref);
770 : 0 : break;
771 : :
772 : 0 : case _AVAHI_SERVER_COLLISION:
773 : 0 : g_warning ("%s(): DNS-SD server collision: %s", G_STRFUNC, error);
774 : 0 : break;
775 : :
776 : 0 : case _AVAHI_SERVER_FAILURE:
777 : 0 : g_warning ("%s(): DNS-SD server failure: %s", G_STRFUNC, error);
778 : 0 : break;
779 : : }
780 : 0 : valent_object_unlock (VALENT_OBJECT (self));
781 : : }
782 : :
783 : : static void
784 : 4 : on_name_appeared (GDBusConnection *connection,
785 : : const char *name,
786 : : const char *name_owner,
787 : : ValentLanDNSSD *self)
788 : : {
789 : 4 : g_autoptr (GCancellable) destroy = NULL;
790 [ - - + - ]: 4 : g_autoptr (GVariant) reply = NULL;
791 [ - - ]: 4 : g_autoptr (GError) error = NULL;
792 : :
793 [ + - ]: 4 : g_assert (VALENT_IS_LAN_DNSSD (self));
794 : :
795 : 4 : destroy = valent_object_ref_cancellable (VALENT_OBJECT (self));
796 : 4 : reply = g_dbus_connection_call_sync (connection,
797 : : AVAHI_DBUS_NAME,
798 : : AVAHI_SERVER2_PATH,
799 : : AVAHI_SERVER2_IFACE,
800 : : "GetState",
801 : : NULL,
802 : : NULL,
803 : : G_DBUS_CALL_FLAGS_NO_AUTO_START,
804 : : -1,
805 : : destroy,
806 : : &error);
807 : :
808 [ - + ]: 4 : if (reply == NULL)
809 : : {
810 [ # # ]: 0 : if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
811 : 0 : g_warning ("%s(): %s", G_STRFUNC, error->message);
812 : :
813 [ # # ]: 0 : return;
814 : : }
815 : :
816 : 4 : valent_object_lock (VALENT_OBJECT (self));
817 : : /* Create a new cancellable, chained to the object's cancellable, so that
818 : : * any operations will be cancelled if the object is destroyed.
819 : : */
820 : 4 : self->connection = g_object_ref (connection);
821 : 4 : self->cancellable = g_cancellable_new ();
822 : 4 : g_signal_connect_object (destroy,
823 : : "cancelled",
824 : : G_CALLBACK (g_cancellable_cancel),
825 : : self->cancellable,
826 : : G_CONNECT_SWAPPED);
827 : :
828 : 4 : g_variant_get (reply, "(i)", &self->server_state);
829 : 8 : self->server_state_id =
830 : 4 : g_dbus_connection_signal_subscribe (self->connection,
831 : : AVAHI_DBUS_NAME,
832 : : AVAHI_SERVER2_IFACE,
833 : : "StateChanged",
834 : : AVAHI_SERVER2_PATH,
835 : : NULL,
836 : : G_DBUS_SIGNAL_FLAGS_NONE,
837 : : _avahi_server_state_changed,
838 : 4 : weak_ref_new (self),
839 : : weak_ref_free);
840 : :
841 : :
842 : : /* If the initial state is "running" call `EntryGroupNew()` and
843 : : * `ServiceBrowserPrepare()`, otherwise wait for a `StateChanged` emission.
844 : : */
845 [ + - ]: 4 : if (self->server_state == _AVAHI_SERVER_RUNNING)
846 : : {
847 : 4 : g_main_context_invoke_full (self->context,
848 : : G_PRIORITY_DEFAULT,
849 : : G_SOURCE_FUNC (_avahi_entry_group_new),
850 : : g_object_ref (self),
851 : : g_object_unref);
852 : 4 : g_main_context_invoke_full (self->context,
853 : : G_PRIORITY_DEFAULT,
854 : : G_SOURCE_FUNC (_avahi_service_browser_prepare),
855 : : g_object_ref (self),
856 : : g_object_unref);
857 : : }
858 [ - + ]: 4 : valent_object_unlock (VALENT_OBJECT (self));
859 : : }
860 : :
861 : : static void
862 : 4 : on_name_vanished (GDBusConnection *connection,
863 : : const char *name,
864 : : ValentLanDNSSD *self)
865 : : {
866 [ + - ]: 4 : g_assert (VALENT_IS_LAN_DNSSD (self));
867 : :
868 : 4 : valent_object_lock (VALENT_OBJECT (self));
869 : 4 : g_cancellable_cancel (self->cancellable);
870 : :
871 [ + - ]: 4 : if (self->connection != NULL)
872 : : {
873 [ + - ]: 4 : if (self->server_state_id != 0)
874 : : {
875 : 4 : g_dbus_connection_signal_unsubscribe (self->connection,
876 : : self->server_state_id);
877 : 4 : self->server_state_id = 0;
878 : : }
879 : :
880 [ + - ]: 4 : if (self->entry_group_state_id != 0)
881 : : {
882 : 4 : g_dbus_connection_signal_unsubscribe (self->connection,
883 : : self->entry_group_state_id);
884 : 4 : self->entry_group_state_id = 0;
885 : : }
886 : :
887 [ + - ]: 4 : if (self->service_browser_event_id != 0)
888 : : {
889 : 4 : g_dbus_connection_signal_unsubscribe (self->connection,
890 : : self->service_browser_event_id);
891 : 4 : self->service_browser_event_id = 0;
892 : : }
893 : :
894 : 4 : self->entry_group_state = _AVAHI_ENTRY_GROUP_UNCOMMITTED;
895 : 4 : self->server_state = _AVAHI_SERVER_INVALID;
896 : :
897 [ + - ]: 4 : g_clear_pointer (&self->service_browser_path, g_free);
898 [ + - ]: 4 : g_clear_pointer (&self->entry_group_path, g_free);
899 [ + - ]: 4 : g_clear_object (&self->connection);
900 [ + - ]: 4 : g_clear_object (&self->cancellable);
901 : : }
902 : 4 : valent_object_unlock (VALENT_OBJECT (self));
903 : 4 : }
904 : :
905 : : static gboolean
906 : 4 : _avahi_client_connect (ValentLanDNSSD *self)
907 : : {
908 : 4 : valent_object_lock (VALENT_OBJECT (self));
909 [ + - ]: 4 : if (self->watcher_id == 0)
910 : : {
911 : 4 : self->watcher_id = g_bus_watch_name (G_BUS_TYPE_SYSTEM,
912 : : AVAHI_DBUS_NAME,
913 : : G_BUS_NAME_WATCHER_FLAGS_NONE,
914 : : (GBusNameAppearedCallback)on_name_appeared,
915 : : (GBusNameVanishedCallback)on_name_vanished,
916 : : self, NULL);
917 : : }
918 : 4 : valent_object_unlock (VALENT_OBJECT (self));
919 : :
920 : 4 : return G_SOURCE_REMOVE;
921 : : }
922 : :
923 : : static gboolean
924 : 4 : _avahi_client_disconnect (ValentLanDNSSD *self)
925 : : {
926 : 4 : valent_object_lock (VALENT_OBJECT (self));
927 : 4 : g_cancellable_cancel (self->cancellable);
928 : :
929 [ + - ]: 4 : if (self->connection != NULL)
930 : : {
931 [ + - ]: 4 : if (self->entry_group_path != NULL)
932 : : {
933 : 4 : g_dbus_connection_call (self->connection,
934 : : AVAHI_DBUS_NAME,
935 : : self->entry_group_path,
936 : : AVAHI_ENTRY_GROUP_IFACE,
937 : : "Free",
938 : : NULL,
939 : : NULL,
940 : : G_DBUS_CALL_FLAGS_NO_AUTO_START,
941 : : -1,
942 : : NULL,
943 : : NULL,
944 : : NULL);
945 : : }
946 : :
947 [ + - ]: 4 : if (self->service_browser_path != NULL)
948 : : {
949 : 4 : g_dbus_connection_call (self->connection,
950 : : AVAHI_DBUS_NAME,
951 : : self->service_browser_path,
952 : : AVAHI_SERVICE_BROWSER_IFACE,
953 : : "Free",
954 : : NULL,
955 : : NULL,
956 : : G_DBUS_CALL_FLAGS_NO_AUTO_START,
957 : : -1,
958 : : NULL,
959 : : NULL,
960 : : NULL);
961 : : }
962 : :
963 [ + - ]: 4 : g_clear_handle_id (&self->watcher_id, g_bus_unwatch_name);
964 : 4 : on_name_vanished (self->connection, AVAHI_DBUS_NAME, self);
965 : : }
966 : 4 : valent_object_unlock (VALENT_OBJECT (self));
967 : :
968 : 4 : return G_SOURCE_REMOVE;
969 : : }
970 : :
971 : : /*
972 : : * ValentLanDNSSD
973 : : */
974 : : static void
975 : 9 : valent_lan_dnssd_set_identity (ValentLanDNSSD *self,
976 : : JsonNode *identity)
977 : : {
978 : 9 : g_autoptr (GPtrArray) txt = NULL;
979 : 9 : const char *id = NULL;
980 : 9 : const char *name = NULL;
981 : 9 : const char *type = NULL;
982 : 9 : gint64 protocol = 0;
983 : 9 : gint64 port = VALENT_LAN_PROTOCOL_PORT;
984 : :
985 [ + - ]: 9 : g_assert (VALENT_IS_LAN_DNSSD (self));
986 [ + + - + ]: 9 : g_assert (identity == NULL || VALENT_IS_PACKET (identity));
987 : :
988 [ + + ]: 9 : if (identity == NULL)
989 : : {
990 [ + + ]: 4 : g_clear_pointer (&self->identity, json_node_unref);
991 : :
992 [ + + ]: 4 : if (self->entry_group_path != NULL)
993 : : {
994 : 1 : g_main_context_invoke_full (self->context,
995 : : G_PRIORITY_DEFAULT,
996 : : G_SOURCE_FUNC (_avahi_entry_group_reset),
997 : : g_object_ref (self),
998 : : g_object_unref);
999 : : }
1000 : 4 : return;
1001 : : }
1002 : :
1003 : : /* Even if the pointers match, assume the contents have changed */
1004 [ + + ]: 5 : if (self->identity != identity)
1005 : : {
1006 [ - + ]: 4 : g_clear_pointer (&self->identity, json_node_unref);
1007 : 4 : self->identity = json_node_ref (identity);
1008 : : }
1009 : :
1010 : : /* Service TXT Record */
1011 : 5 : txt = g_ptr_array_new ();
1012 : :
1013 [ + - ]: 5 : if (valent_packet_get_string (identity, "deviceId", &id))
1014 : 5 : g_ptr_array_add (txt, txt_new_str ("id", id));
1015 : :
1016 [ + - ]: 5 : if (valent_packet_get_string (identity, "deviceName", &name))
1017 : 5 : g_ptr_array_add (txt, txt_new_str ("name", name));
1018 : :
1019 [ + - ]: 5 : if (valent_packet_get_string (identity, "deviceType", &type))
1020 : 5 : g_ptr_array_add (txt, txt_new_str ("type", type));
1021 : :
1022 [ + - ]: 5 : if (valent_packet_get_int (identity, "protocolVersion", &protocol))
1023 : 5 : g_ptr_array_add (txt, txt_new_uint ("protocol", (uint32_t)protocol));
1024 : :
1025 [ + + ]: 5 : g_clear_pointer (&self->txt, g_variant_unref);
1026 : 10 : self->txt = g_variant_new_array (G_VARIANT_TYPE_BYTESTRING,
1027 : 5 : (GVariant * const *)txt->pdata,
1028 : 5 : txt->len);
1029 : 5 : g_variant_ref_sink (self->txt);
1030 : :
1031 : : /* Service Name and Port */
1032 : 5 : g_set_str (&self->name, id);
1033 : :
1034 [ + - ]: 5 : if (valent_packet_get_int (identity, "tcpPort", &port))
1035 : 5 : self->port = (uint16_t)port;
1036 : :
1037 [ + + ]: 5 : if (self->entry_group_path != NULL)
1038 : : {
1039 : 1 : g_main_context_invoke_full (self->context,
1040 : : G_PRIORITY_DEFAULT,
1041 : : G_SOURCE_FUNC (_avahi_entry_group_add_service),
1042 : : g_object_ref (self),
1043 : : g_object_unref);
1044 : : }
1045 : : }
1046 : :
1047 : : static void
1048 : 4 : valent_lan_dnssd_set_service_type (ValentLanDNSSD *self,
1049 : : const char *type)
1050 : : {
1051 [ + - ]: 4 : g_assert (VALENT_IS_LAN_DNSSD (self));
1052 [ + - - + ]: 4 : g_assert (type == NULL || *type != '\0');
1053 : :
1054 [ - + ]: 4 : if (type == NULL)
1055 : 0 : type = KDECONNECT_UDP_SERVICE_TYPE;
1056 : :
1057 [ + + ]: 4 : if (g_set_str (&self->type, type))
1058 : : {
1059 : 1 : valent_object_notify_by_pspec (VALENT_OBJECT (self),
1060 : : properties [PROP_SERVICE_TYPE]);
1061 : : }
1062 : 4 : }
1063 : :
1064 : : /*
1065 : : * ValentObject
1066 : : */
1067 : : static void
1068 : 4 : valent_lan_dnssd_destroy (ValentObject *object)
1069 : : {
1070 : 4 : ValentLanDNSSD *self = VALENT_LAN_DNSSD (object);
1071 : :
1072 : 4 : _avahi_client_disconnect (self);
1073 : :
1074 [ + - ]: 4 : g_clear_pointer (&self->context, g_main_context_unref);
1075 [ + - ]: 4 : g_clear_pointer (&self->name, g_free);
1076 [ + - ]: 4 : g_clear_pointer (&self->type, g_free);
1077 [ + - ]: 4 : g_clear_pointer (&self->txt, g_variant_unref);
1078 : :
1079 : 4 : VALENT_OBJECT_CLASS (valent_lan_dnssd_parent_class)->destroy (object);
1080 : 4 : }
1081 : :
1082 : : /*
1083 : : * GObject
1084 : : */
1085 : : static void
1086 : 4 : valent_lan_dnssd_finalize (GObject *object)
1087 : : {
1088 : 4 : ValentLanDNSSD *self = VALENT_LAN_DNSSD (object);
1089 : :
1090 : 4 : valent_object_lock (VALENT_OBJECT (object));
1091 [ + - ]: 4 : g_clear_pointer (&self->items, g_ptr_array_unref);
1092 [ + + ]: 4 : g_clear_pointer (&self->identity, json_node_unref);
1093 : 4 : valent_object_unlock (VALENT_OBJECT (object));
1094 : :
1095 : 4 : G_OBJECT_CLASS (valent_lan_dnssd_parent_class)->finalize (object);
1096 : 4 : }
1097 : :
1098 : : static void
1099 : 2 : valent_lan_dnssd_get_property (GObject *object,
1100 : : guint prop_id,
1101 : : GValue *value,
1102 : : GParamSpec *pspec)
1103 : : {
1104 : 2 : ValentLanDNSSD *self = VALENT_LAN_DNSSD (object);
1105 : :
1106 [ + + - ]: 2 : switch ((ValentLanDNSSDProperty)prop_id)
1107 : : {
1108 : : case PROP_IDENTITY:
1109 : 1 : valent_object_lock (VALENT_OBJECT (self));
1110 : 1 : g_value_set_boxed (value, self->identity);
1111 : 1 : valent_object_unlock (VALENT_OBJECT (self));
1112 : 1 : break;
1113 : :
1114 : : case PROP_SERVICE_TYPE:
1115 : 1 : valent_object_lock (VALENT_OBJECT (self));
1116 : 1 : g_value_set_string (value, self->type);
1117 : 1 : valent_object_unlock (VALENT_OBJECT (self));
1118 : 1 : break;
1119 : :
1120 : 0 : default:
1121 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1122 : : }
1123 : 2 : }
1124 : :
1125 : : static void
1126 : 13 : valent_lan_dnssd_set_property (GObject *object,
1127 : : guint prop_id,
1128 : : const GValue *value,
1129 : : GParamSpec *pspec)
1130 : : {
1131 : 13 : ValentLanDNSSD *self = VALENT_LAN_DNSSD (object);
1132 : :
1133 [ + + - ]: 13 : switch ((ValentLanDNSSDProperty)prop_id)
1134 : : {
1135 : : case PROP_IDENTITY:
1136 : 9 : valent_object_lock (VALENT_OBJECT (self));
1137 : 9 : valent_lan_dnssd_set_identity (self, g_value_get_boxed (value));
1138 : 9 : valent_object_unlock (VALENT_OBJECT (self));
1139 : 9 : break;
1140 : :
1141 : : case PROP_SERVICE_TYPE:
1142 : 4 : valent_object_lock (VALENT_OBJECT (self));
1143 : 4 : valent_lan_dnssd_set_service_type (self, g_value_get_string (value));
1144 : 4 : valent_object_unlock (VALENT_OBJECT (self));
1145 : 4 : break;
1146 : :
1147 : 0 : default:
1148 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1149 : : }
1150 : 13 : }
1151 : :
1152 : : static void
1153 : 2 : valent_lan_dnssd_class_init (ValentLanDNSSDClass *klass)
1154 : : {
1155 : 2 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
1156 : 2 : ValentObjectClass *vobject_class = VALENT_OBJECT_CLASS (klass);
1157 : :
1158 : 2 : object_class->finalize = valent_lan_dnssd_finalize;
1159 : 2 : object_class->get_property = valent_lan_dnssd_get_property;
1160 : 2 : object_class->set_property = valent_lan_dnssd_set_property;
1161 : :
1162 : 2 : vobject_class->destroy = valent_lan_dnssd_destroy;
1163 : :
1164 : : /**
1165 : : * ValentLanDNSSD:identity:
1166 : : *
1167 : : * The KDE Connect packet holding the local identity.
1168 : : */
1169 : 4 : properties [PROP_IDENTITY] =
1170 : 2 : g_param_spec_boxed ("identity", NULL, NULL,
1171 : : JSON_TYPE_NODE,
1172 : : (G_PARAM_READWRITE |
1173 : : G_PARAM_EXPLICIT_NOTIFY |
1174 : : G_PARAM_STATIC_STRINGS));
1175 : :
1176 : : /**
1177 : : * ValentLanDNSSD:service-type:
1178 : : *
1179 : : * The DNS-SD service type to register and observe.
1180 : : */
1181 : 4 : properties [PROP_SERVICE_TYPE] =
1182 : 2 : g_param_spec_string ("service-type", NULL, NULL,
1183 : : KDECONNECT_UDP_SERVICE_TYPE,
1184 : : (G_PARAM_READWRITE |
1185 : : G_PARAM_CONSTRUCT_ONLY |
1186 : : G_PARAM_EXPLICIT_NOTIFY |
1187 : : G_PARAM_STATIC_STRINGS));
1188 : :
1189 : 2 : g_object_class_install_properties (object_class, G_N_ELEMENTS (properties), properties);
1190 : 2 : }
1191 : :
1192 : : static void
1193 : 4 : valent_lan_dnssd_init (ValentLanDNSSD *self)
1194 : : {
1195 : 4 : valent_object_lock (VALENT_OBJECT (self));
1196 : 4 : self->items = g_ptr_array_new_with_free_func (g_object_unref);
1197 : 4 : self->type = g_strdup (KDECONNECT_UDP_SERVICE_TYPE);
1198 : 4 : valent_object_unlock (VALENT_OBJECT (self));
1199 : 4 : }
1200 : :
1201 : : /**
1202 : : * valent_lan_dnssd_new:
1203 : : * @identity: (nullable): a KDE Connect identity packet
1204 : : *
1205 : : * Create a DNS-SD adapter for @identity.
1206 : : *
1207 : : * Returns: (transfer full): a `GListModel`
1208 : : */
1209 : : GListModel *
1210 : 3 : valent_lan_dnssd_new (JsonNode *identity)
1211 : : {
1212 [ - + - - ]: 3 : g_return_val_if_fail (identity == NULL || VALENT_IS_PACKET (identity), NULL);
1213 : :
1214 : 3 : return g_object_new (VALENT_TYPE_LAN_DNSSD,
1215 : : "identity", identity,
1216 : : NULL);
1217 : : }
1218 : :
1219 : : /**
1220 : : * valent_lan_dnssd_attach:
1221 : : * @self: an `ValentLanDNSSD`
1222 : : * @context: (nullable): a `GMainContext`
1223 : : *
1224 : : * Start the DNS-SD adapter in @context.
1225 : : *
1226 : : * If @context is %NULL, the service will run in the thread-default main
1227 : : * context, as returned by [type@GLib.MainContext.ref_thread_default].
1228 : : */
1229 : : void
1230 : 4 : valent_lan_dnssd_attach (ValentLanDNSSD *self,
1231 : : GMainContext *context)
1232 : : {
1233 [ + - ]: 4 : g_return_if_fail (VALENT_IS_LAN_DNSSD (self));
1234 : :
1235 : 4 : valent_object_lock (VALENT_OBJECT (self));
1236 [ + + ]: 4 : if (context == NULL)
1237 : : {
1238 [ - + ]: 1 : g_clear_pointer (&self->context, g_main_context_unref);
1239 : 1 : self->context = g_main_context_ref_thread_default ();
1240 : : }
1241 [ + - ]: 3 : else if (self->context != context)
1242 : : {
1243 [ - + ]: 3 : g_clear_pointer (&self->context, g_main_context_unref);
1244 : 3 : self->context = g_main_context_ref (context);
1245 : : }
1246 : :
1247 : 4 : g_main_context_invoke_full (self->context,
1248 : : G_PRIORITY_DEFAULT,
1249 : : G_SOURCE_FUNC (_avahi_client_connect),
1250 : : g_object_ref (self),
1251 : : g_object_unref);
1252 : 4 : valent_object_unlock (VALENT_OBJECT (self));
1253 : : }
|