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