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-bluez-channel-service"
5 : :
6 : : #include "config.h"
7 : :
8 : : #include <valent.h>
9 : :
10 : : #include "valent-bluez-device.h"
11 : : #include "valent-bluez-muxer.h"
12 : : #include "valent-bluez-profile.h"
13 : :
14 : : #include "valent-bluez-channel-service.h"
15 : :
16 : : #define DEFAULT_BUFFER_SIZE 4096
17 : :
18 : :
19 : : struct _ValentBluezChannelService
20 : : {
21 : : ValentChannelService parent_instance;
22 : :
23 : : GDBusProxy *proxy;
24 : : ValentBluezProfile *profile;
25 : : GHashTable *devices;
26 : : GHashTable *muxers;
27 : : };
28 : :
29 : : static void g_async_initable_iface_init (GAsyncInitableIface *iface);
30 : :
31 [ + + + - ]: 14 : G_DEFINE_FINAL_TYPE_WITH_CODE (ValentBluezChannelService, valent_bluez_channel_service, VALENT_TYPE_CHANNEL_SERVICE,
32 : : G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, g_async_initable_iface_init))
33 : :
34 : : /*
35 : : * ValentBluezProfile Callbacks
36 : : */
37 : : static void
38 : 2 : valent_bluez_muxer_handshake_cb (ValentBluezMuxer *muxer,
39 : : GAsyncResult *result,
40 : : gpointer user_data)
41 : : {
42 : 2 : g_autoptr (GTask) task = G_TASK (g_steal_pointer (&user_data));
43 : 2 : ValentBluezChannelService *self = g_task_get_source_object (task);
44 : 2 : const char *object_path = g_task_get_task_data (task);
45 [ + - ]: 2 : g_autoptr (ValentChannel) channel = NULL;
46 [ + - ]: 2 : g_autoptr (GError) error = NULL;
47 : :
48 : 2 : channel = valent_bluez_muxer_handshake_finish (muxer, result, &error);
49 [ + - ]: 2 : if (channel != NULL)
50 : : {
51 [ - + ]: 2 : g_hash_table_replace (self->muxers,
52 : 2 : g_strdup (object_path),
53 : : g_object_ref (muxer));
54 : 2 : valent_channel_service_channel (VALENT_CHANNEL_SERVICE (self), channel);
55 : : }
56 [ # # ]: 0 : else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
57 : : {
58 : 0 : g_warning ("%s(): \"%s\": %s", G_STRFUNC, object_path, error->message);
59 : : }
60 : :
61 [ - + ]: 2 : g_task_return_boolean (task, TRUE);
62 : 2 : }
63 : :
64 : : static void
65 : 2 : on_connection_opened (ValentBluezProfile *profile,
66 : : GSocketConnection *connection,
67 : : const char *object_path,
68 : : gpointer user_data)
69 : : {
70 : 2 : ValentBluezChannelService *self = VALENT_BLUEZ_CHANNEL_SERVICE (user_data);
71 : 4 : g_autoptr (JsonNode) identity = NULL;
72 [ + - ]: 2 : g_autoptr (GTask) task = NULL;
73 [ + - ]: 2 : g_autoptr (GCancellable) cancellable = NULL;
74 : :
75 [ - + ]: 2 : g_assert (VALENT_IS_BLUEZ_CHANNEL_SERVICE (self));
76 [ + - + - : 2 : g_assert (G_IS_SOCKET_CONNECTION (connection));
- + - - ]
77 [ + - ]: 2 : g_assert (object_path != NULL);
78 : :
79 : 2 : task = g_task_new (self, NULL, NULL, NULL);
80 [ - + ]: 4 : g_task_set_task_data (task, g_strdup (object_path), g_free);
81 [ + - ]: 2 : g_task_set_source_tag (task, on_connection_opened);
82 : :
83 : 2 : cancellable = valent_object_ref_cancellable (VALENT_OBJECT (self));
84 : 2 : identity = valent_channel_service_ref_identity (VALENT_CHANNEL_SERVICE (self));
85 [ + - ]: 2 : valent_bluez_muxer_handshake (G_IO_STREAM (connection),
86 : : identity,
87 : : cancellable,
88 : : (GAsyncReadyCallback)valent_bluez_muxer_handshake_cb,
89 : : g_object_ref (task));
90 : 2 : }
91 : :
92 : : static void
93 : 0 : on_connection_closed (ValentBluezProfile *profile,
94 : : const char *object_path,
95 : : gpointer user_data)
96 : : {
97 : 0 : ValentBluezChannelService *self = VALENT_BLUEZ_CHANNEL_SERVICE (user_data);
98 : 0 : gpointer key, value;
99 : :
100 [ # # ]: 0 : g_assert (VALENT_IS_BLUEZ_CHANNEL_SERVICE (self));
101 [ # # ]: 0 : g_assert (object_path != NULL);
102 : :
103 [ # # ]: 0 : if (g_hash_table_steal_extended (self->muxers, object_path, &key, &value))
104 : : {
105 : 0 : valent_bluez_muxer_close (value, NULL, NULL);
106 : 0 : g_free (key);
107 : 0 : g_object_unref (value);
108 : : }
109 : 0 : }
110 : :
111 : : /*
112 : : * GDBusObjectManager
113 : : */
114 : : static void
115 : 6 : on_interfaces_added (ValentBluezChannelService *self,
116 : : const char *object_path,
117 : : GVariant *interfaces)
118 : : {
119 : 6 : GDBusConnection *connection;
120 : 6 : GVariantIter iter;
121 : 6 : const char *interface;
122 : 6 : GVariant *properties;
123 : :
124 : 6 : connection = g_dbus_proxy_get_connection (self->proxy);
125 : 6 : g_variant_iter_init (&iter, interfaces);
126 [ + + ]: 16 : while (g_variant_iter_next (&iter, "{&s@a{sv}}", &interface, &properties))
127 : : {
128 [ + + ]: 10 : if (g_strcmp0 (interface, "org.bluez.Device1") == 0)
129 : : {
130 : 12 : g_autoptr (ValentBluezDevice) device = NULL;
131 : :
132 : 2 : device = valent_bluez_device_new (connection,
133 : : object_path,
134 : : properties);
135 [ - + + - ]: 4 : g_hash_table_replace (self->devices,
136 : 2 : g_strdup (object_path),
137 : : g_object_ref (device));
138 : : }
139 : :
140 : 10 : g_variant_unref (properties);
141 : : }
142 : 6 : }
143 : :
144 : : static void
145 : 0 : on_interfaces_removed (ValentBluezChannelService *self,
146 : : const char *object_path,
147 : : const char **interfaces)
148 : : {
149 : 0 : gpointer key, value;
150 : :
151 [ # # ]: 0 : g_assert (VALENT_IS_BLUEZ_CHANNEL_SERVICE (self));
152 [ # # ]: 0 : g_assert (object_path != NULL);
153 : :
154 [ # # ]: 0 : if (!g_strv_contains (interfaces, "org.bluez.Device1"))
155 : 0 : return;
156 : :
157 [ # # ]: 0 : if (g_hash_table_steal_extended (self->muxers, object_path, &key, &value))
158 : : {
159 : 0 : valent_bluez_muxer_close (value, NULL, NULL);
160 : 0 : g_free (key);
161 : 0 : g_object_unref (value);
162 : : }
163 : :
164 : 0 : g_hash_table_remove (self->devices, object_path);
165 : : }
166 : :
167 : : static void
168 : 0 : on_g_signal (GDBusProxy *proxy,
169 : : char *sender_name,
170 : : char *signal_name,
171 : : GVariant *parameters,
172 : : ValentBluezChannelService *self)
173 : : {
174 : 0 : g_autofree char *name_owner = NULL;
175 : :
176 [ # # ]: 0 : g_assert (VALENT_IS_BLUEZ_CHANNEL_SERVICE (self));
177 : :
178 : 0 : name_owner = g_dbus_proxy_get_name_owner (proxy);
179 [ # # ]: 0 : if (name_owner != NULL)
180 : 0 : return;
181 : :
182 [ # # ]: 0 : if (g_strcmp0 (signal_name, "InterfacesAdded") == 0)
183 : : {
184 : 0 : const char *object_path;
185 : 0 : GVariant *ifaces_props;
186 : :
187 : 0 : g_variant_get (parameters, "(&o@a{sa{sv}})", &object_path, &ifaces_props);
188 : 0 : on_interfaces_added (self, object_path, ifaces_props);
189 : : }
190 [ # # ]: 0 : else if (g_strcmp0 (signal_name, "InterfacesRemoved") == 0)
191 : : {
192 : 0 : const char *object_path;
193 : 0 : const char **interfaces;
194 : :
195 : 0 : g_variant_get (parameters, "(&o^a&s)", &object_path, &interfaces);
196 : 0 : on_interfaces_removed (self, object_path, interfaces);
197 : : }
198 : : }
199 : :
200 : : static void
201 : 2 : activate_cb (GObject *object,
202 : : GAsyncResult *result,
203 : : gpointer user_data)
204 : : {
205 : 2 : ValentBluezChannelService *self = g_task_get_source_object (G_TASK (result));
206 : 4 : g_autoptr (GError) error = NULL;
207 : :
208 [ + - ]: 2 : if (g_task_propagate_boolean (G_TASK (result), &error))
209 : : {
210 : 2 : valent_extension_plugin_state_changed (VALENT_EXTENSION (self),
211 : : VALENT_PLUGIN_STATE_ACTIVE,
212 : : NULL);
213 : : }
214 : : else
215 : : {
216 : 0 : valent_extension_plugin_state_changed (VALENT_EXTENSION (self),
217 : : VALENT_PLUGIN_STATE_ERROR,
218 : : error);
219 : : }
220 : 2 : }
221 : :
222 : : static void
223 : 2 : get_managed_objects_cb (GDBusProxy *proxy,
224 : : GAsyncResult *result,
225 : : gpointer user_data)
226 : : {
227 : 4 : g_autoptr (GTask) task = G_TASK (g_steal_pointer (&user_data));
228 : 2 : ValentBluezChannelService *self = g_task_get_source_object (task);
229 [ + - ]: 2 : g_autoptr (GVariant) reply = NULL;
230 [ - - ]: 2 : g_autoptr (GVariant) objects = NULL;
231 : 2 : GError *error = NULL;
232 : 2 : const char *object_path;
233 : 2 : GVariant *interfaces;
234 : 2 : GVariantIter iter;
235 : :
236 : 2 : reply = g_dbus_proxy_call_finish (proxy, result, &error);
237 [ - + ]: 2 : if (reply == NULL)
238 : : {
239 : 0 : g_task_return_error (task, g_steal_pointer (&error));
240 [ # # ]: 0 : return;
241 : : }
242 : :
243 : 2 : objects = g_variant_get_child_value (reply, 0);
244 : 2 : g_variant_iter_init (&iter, objects);
245 [ + + ]: 8 : while (g_variant_iter_next (&iter, "{&o@a{sa{sv}}}", &object_path, &interfaces))
246 : : {
247 : 6 : on_interfaces_added (self, object_path, interfaces);
248 : 6 : g_variant_unref (interfaces);
249 : : }
250 : :
251 [ + - ]: 2 : g_task_return_boolean (task, TRUE);
252 : : }
253 : :
254 : : static inline void
255 : 2 : valent_bluez_profile_register_cb (ValentBluezProfile *profile,
256 : : GAsyncResult *result,
257 : : gpointer user_data)
258 : : {
259 : 4 : g_autoptr (GTask) task = G_TASK (g_steal_pointer (&user_data));
260 : 2 : ValentBluezChannelService *self = g_task_get_source_object (task);
261 : 2 : GCancellable *cancellable = g_task_get_cancellable (task);
262 : 2 : GError *error = NULL;
263 : :
264 [ - + ]: 2 : if (!valent_bluez_profile_register_finish (profile, result, &error))
265 : : {
266 : 0 : g_task_return_error (task, g_steal_pointer (&error));
267 [ # # ]: 0 : return;
268 : : }
269 : :
270 [ + - ]: 2 : g_dbus_proxy_call (self->proxy,
271 : : "GetManagedObjects",
272 : : NULL,
273 : : G_DBUS_CALL_FLAGS_NONE,
274 : : -1,
275 : : cancellable,
276 : : (GAsyncReadyCallback)get_managed_objects_cb,
277 : : g_object_ref (task));
278 : : }
279 : :
280 : : static void
281 : 2 : on_name_owner_changed (GDBusProxy *proxy,
282 : : GParamSpec *pspec,
283 : : ValentBluezChannelService *self)
284 : : {
285 : 4 : g_autofree char *name_owner = NULL;
286 : :
287 [ - + ]: 2 : g_assert (VALENT_IS_BLUEZ_CHANNEL_SERVICE (self));
288 : :
289 : 2 : name_owner = g_dbus_proxy_get_name_owner (proxy);
290 [ + - ]: 2 : if (name_owner != NULL)
291 : : {
292 : 2 : g_autoptr (GTask) task = NULL;
293 [ + - ]: 2 : g_autoptr (GCancellable) destroy = NULL;
294 : :
295 : 2 : destroy = valent_object_ref_cancellable (VALENT_OBJECT (self));
296 : 2 : task = g_task_new (self, destroy, activate_cb, NULL);
297 [ + - ]: 2 : g_task_set_source_tag (task, on_name_owner_changed);
298 [ + - ]: 2 : valent_bluez_profile_register (self->profile,
299 : : g_dbus_proxy_get_connection (proxy),
300 : : destroy,
301 : : (GAsyncReadyCallback)valent_bluez_profile_register_cb,
302 : : g_object_ref (task));
303 : : }
304 : : else
305 : : {
306 : 0 : GHashTableIter iter;
307 : 0 : ValentBluezMuxer *connection;
308 : :
309 : 0 : g_hash_table_iter_init (&iter, self->muxers);
310 [ # # ]: 0 : while (g_hash_table_iter_next (&iter, NULL, (void **)&connection))
311 : : {
312 : 0 : valent_bluez_muxer_close (connection, NULL, NULL);
313 : 0 : g_hash_table_iter_remove (&iter);
314 : : }
315 : :
316 : 0 : g_hash_table_remove_all (self->devices);
317 : 0 : valent_bluez_profile_unregister (self->profile);
318 : 0 : valent_extension_plugin_state_changed (VALENT_EXTENSION (self),
319 : : VALENT_PLUGIN_STATE_INACTIVE,
320 : : NULL);
321 : : }
322 : 2 : }
323 : :
324 : : /*
325 : : * ValentChannelService
326 : : */
327 : : static void
328 : 2 : valent_bluez_channel_service_build_identity (ValentChannelService *service)
329 : : {
330 : 2 : ValentChannelServiceClass *klass;
331 : 4 : g_autoptr (JsonNode) identity = NULL;
332 : :
333 [ - + ]: 2 : g_assert (VALENT_IS_BLUEZ_CHANNEL_SERVICE (service));
334 : :
335 : : /* Chain-up */
336 : 2 : klass = VALENT_CHANNEL_SERVICE_CLASS (valent_bluez_channel_service_parent_class);
337 : 2 : klass->build_identity (service);
338 : :
339 : : /* Set the certificate on the packet
340 : : */
341 : 2 : identity = valent_channel_service_ref_identity (service);
342 [ + - ]: 2 : if (identity != NULL)
343 : : {
344 : 2 : g_autoptr (GTlsCertificate) certificate = NULL;
345 [ + - ]: 2 : g_autofree char *certificate_pem = NULL;
346 : 2 : JsonObject *body;
347 : :
348 : 2 : certificate = valent_channel_service_ref_certificate (service);
349 : 2 : g_object_get (certificate, "certificate-pem", &certificate_pem, NULL);
350 : :
351 : 2 : body = valent_packet_get_body (identity);
352 : 2 : json_object_set_string_member (body, "certificate", certificate_pem);
353 : : }
354 : 2 : }
355 : :
356 : : static void
357 : 2 : valent_bluez_channel_service_identify (ValentChannelService *service,
358 : : const char *target)
359 : : {
360 : 2 : ValentBluezChannelService *self = VALENT_BLUEZ_CHANNEL_SERVICE (service);
361 : 2 : g_autofree char *name_owner = NULL;
362 : 2 : ValentBluezDevice *device;
363 : :
364 [ - + ]: 2 : g_assert (VALENT_IS_BLUEZ_CHANNEL_SERVICE (self));
365 : :
366 : 2 : name_owner = g_dbus_proxy_get_name_owner (self->proxy);
367 [ - + ]: 2 : if (name_owner == NULL)
368 : 0 : return;
369 : :
370 [ - + ]: 2 : if (target != NULL)
371 : : {
372 : 0 : device = g_hash_table_lookup (self->devices, target);
373 [ # # ]: 0 : if (device != NULL)
374 : 0 : valent_bluez_device_connect (device);
375 : : }
376 : : else
377 : : {
378 : 2 : GHashTableIter iter;
379 : :
380 : 2 : g_hash_table_iter_init (&iter, self->devices);
381 [ + + ]: 4 : while (g_hash_table_iter_next (&iter, NULL, (void **)&device))
382 : 2 : valent_bluez_device_connect (device);
383 : : }
384 : : }
385 : :
386 : : static void
387 : 2 : g_dbus_proxy_new_for_bus_cb (GObject *object,
388 : : GAsyncResult *result,
389 : : gpointer user_data)
390 : : {
391 : 2 : g_autoptr (GTask) task = G_TASK (user_data);
392 : 2 : ValentBluezChannelService *self = g_task_get_source_object (task);
393 : 2 : GError *error = NULL;
394 : :
395 : 2 : self->proxy = g_dbus_proxy_new_for_bus_finish (result, &error);
396 [ - + ]: 2 : if (self->proxy == NULL)
397 : : {
398 : 0 : g_task_return_error (task, g_steal_pointer (&error));
399 [ # # ]: 0 : return;
400 : : }
401 : :
402 : 2 : g_signal_connect_object (self->proxy,
403 : : "g-signal",
404 : : G_CALLBACK (on_g_signal),
405 : : self,
406 : : G_CONNECT_DEFAULT);
407 : 2 : g_signal_connect_object (self->proxy,
408 : : "notify::g-name-owner",
409 : : G_CALLBACK (on_name_owner_changed),
410 : : self,
411 : : G_CONNECT_DEFAULT);
412 : 2 : on_name_owner_changed (self->proxy, NULL, self);
413 : :
414 [ + - ]: 2 : g_task_return_boolean (task, TRUE);
415 : : }
416 : :
417 : : /*
418 : : * GAsyncInitable
419 : : */
420 : : static void
421 : 2 : valent_bluez_channel_service_init_async (GAsyncInitable *initable,
422 : : int io_priority,
423 : : GCancellable *cancellable,
424 : : GAsyncReadyCallback callback,
425 : : gpointer user_data)
426 : : {
427 : 4 : g_autoptr (GTask) task = NULL;
428 : :
429 [ - + ]: 2 : g_assert (VALENT_IS_CHANNEL_SERVICE (initable));
430 [ - + - - : 2 : g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- - - - ]
431 : :
432 : 2 : task = g_task_new (initable, cancellable, callback, user_data);
433 : 2 : g_task_set_priority (task, io_priority);
434 [ + - ]: 2 : g_task_set_source_tag (task, valent_bluez_channel_service_init_async);
435 : :
436 [ + - ]: 2 : g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
437 : : G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
438 : : NULL,
439 : : "org.bluez",
440 : : "/",
441 : : "org.freedesktop.DBus.ObjectManager",
442 : : cancellable,
443 : : (GAsyncReadyCallback)g_dbus_proxy_new_for_bus_cb,
444 : : g_object_ref (task));
445 : 2 : }
446 : :
447 : : static void
448 : 1 : g_async_initable_iface_init (GAsyncInitableIface *iface)
449 : : {
450 : 1 : iface->init_async = valent_bluez_channel_service_init_async;
451 : 1 : }
452 : :
453 : : /*
454 : : * ValentObject
455 : : */
456 : : static void
457 : 4 : valent_bluez_channel_service_destroy (ValentObject *object)
458 : : {
459 : 4 : ValentBluezChannelService *self = VALENT_BLUEZ_CHANNEL_SERVICE (object);
460 : :
461 [ + + ]: 4 : if (self->proxy != NULL)
462 : : {
463 : 2 : g_signal_handlers_disconnect_by_data (self->proxy, self);
464 [ + - ]: 2 : g_clear_object (&self->proxy);
465 : : }
466 : :
467 [ + - ]: 4 : if (self->profile != NULL)
468 : : {
469 : 4 : g_signal_handlers_disconnect_by_data (self->profile, self);
470 : 4 : valent_bluez_profile_unregister (self->profile);
471 : : }
472 : :
473 : 4 : VALENT_OBJECT_CLASS (valent_bluez_channel_service_parent_class)->destroy (object);
474 : 4 : }
475 : :
476 : : /*
477 : : * GObject
478 : : */
479 : : static void
480 : 2 : valent_bluez_channel_service_finalize (GObject *object)
481 : : {
482 : 2 : ValentBluezChannelService *self = VALENT_BLUEZ_CHANNEL_SERVICE (object);
483 : :
484 [ - + ]: 2 : g_clear_object (&self->proxy);
485 [ + - ]: 2 : g_clear_object (&self->profile);
486 [ + - ]: 2 : g_clear_pointer (&self->devices, g_hash_table_unref);
487 [ + - ]: 2 : g_clear_pointer (&self->muxers, g_hash_table_unref);
488 : :
489 : 2 : G_OBJECT_CLASS (valent_bluez_channel_service_parent_class)->finalize (object);
490 : 2 : }
491 : :
492 : : static void
493 : 1 : valent_bluez_channel_service_class_init (ValentBluezChannelServiceClass *klass)
494 : : {
495 : 1 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
496 : 1 : ValentObjectClass *vobject_class = VALENT_OBJECT_CLASS (klass);
497 : 1 : ValentChannelServiceClass *service_class = VALENT_CHANNEL_SERVICE_CLASS (klass);
498 : :
499 : 1 : object_class->finalize = valent_bluez_channel_service_finalize;
500 : :
501 : 1 : vobject_class->destroy = valent_bluez_channel_service_destroy;
502 : :
503 : 1 : service_class->build_identity = valent_bluez_channel_service_build_identity;
504 : 1 : service_class->identify = valent_bluez_channel_service_identify;
505 : : }
506 : :
507 : : static void
508 : 2 : valent_bluez_channel_service_init (ValentBluezChannelService *self)
509 : : {
510 : 2 : self->profile = valent_bluez_profile_new ();
511 : 2 : g_signal_connect_object (self->profile,
512 : : "connection-opened",
513 : : G_CALLBACK (on_connection_opened),
514 : : self,
515 : : G_CONNECT_DEFAULT);
516 : 2 : g_signal_connect_object (self->profile,
517 : : "connection-closed",
518 : : G_CALLBACK (on_connection_closed),
519 : : self,
520 : : G_CONNECT_DEFAULT);
521 : :
522 : 2 : self->devices = g_hash_table_new_full (g_str_hash,
523 : : g_str_equal,
524 : : g_free,
525 : : g_object_unref);
526 : 2 : self->muxers = g_hash_table_new_full (g_str_hash,
527 : : g_str_equal,
528 : : g_free,
529 : : g_object_unref);
530 : 2 : }
531 : :
|