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