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-channel-service"
5 : :
6 : : #include "config.h"
7 : :
8 : : #include <gio/gio.h>
9 : : #include <json-glib/json-glib.h>
10 : : #include <libpeas.h>
11 : : #include <libvalent-core.h>
12 : :
13 : : #include "valent-certificate.h"
14 : : #include "valent-channel.h"
15 : : #include "valent-device.h"
16 : : #include "valent-device-common.h"
17 : : #include "valent-packet.h"
18 : :
19 : : #include "valent-channel-service.h"
20 : :
21 : : /**
22 : : * ValentChannelService:
23 : : *
24 : : * An abstract base class for connection backends.
25 : : *
26 : : * `ValentChannelService` is a base class for plugins that implement an interface
27 : : * to negotiate connections with other devices.
28 : : *
29 : : * ## Implementation Notes
30 : : *
31 : : * Implementations may safely invoke [method@Valent.ChannelService.channel] from
32 : : * any thread; it is guaranteed to be emitted in the main thread.
33 : : *
34 : : * ## `.plugin` File
35 : : *
36 : : * Channel services have no special fields in the `.plugin` file.
37 : : *
38 : : * Since: 1.0
39 : : */
40 : :
41 : : typedef struct
42 : : {
43 : : GTlsCertificate *certificate;
44 : : const char *id;
45 : : JsonNode *identity;
46 : : char *name;
47 : : GSettings *settings;
48 : : GHashTable *channels;
49 : : } ValentChannelServicePrivate;
50 : :
51 [ + + + - ]: 781 : G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (ValentChannelService, valent_channel_service, VALENT_TYPE_EXTENSION);
52 : :
53 : : typedef enum {
54 : : PROP_CERTIFICATE = 1,
55 : : PROP_ID,
56 : : PROP_IDENTITY,
57 : : } ValentChannelServiceProperty;
58 : :
59 : : static GParamSpec *properties[PROP_IDENTITY + 1] = { NULL, };
60 : :
61 : : typedef enum {
62 : : CHANNEL,
63 : : } ValentChannelServiceSignal;
64 : :
65 : : static guint signals[CHANNEL + 1] = { 0, };
66 : :
67 : :
68 : : static inline void
69 : 10 : _valent_object_deref (gpointer data)
70 : : {
71 [ + + ]: 10 : if (!valent_object_in_destruction (VALENT_OBJECT (data)))
72 : 9 : valent_object_destroy (VALENT_OBJECT (data));
73 : :
74 : 10 : g_object_unref (data);
75 : 10 : }
76 : :
77 : : static void
78 : 1 : on_channel_destroyed (ValentChannelService *self,
79 : : ValentChannel *channel)
80 : : {
81 : 1 : ValentChannelServicePrivate *priv = valent_channel_service_get_instance_private (self);
82 : 2 : g_autoptr (GTlsCertificate) certificate = NULL;
83 : 1 : const char *device_id = NULL;
84 : :
85 [ - + ]: 1 : g_assert (VALENT_IS_CHANNEL_SERVICE (self));
86 [ + - ]: 1 : g_assert (VALENT_IS_CHANNEL (channel));
87 : :
88 : 1 : certificate = valent_channel_ref_certificate (VALENT_CHANNEL (channel));
89 : 1 : device_id = valent_certificate_get_common_name (certificate);
90 [ + - ]: 1 : g_hash_table_remove (priv->channels, device_id);
91 : 1 : }
92 : :
93 : : static gboolean
94 : 10 : valent_channel_service_verify_channel (ValentChannelService *self,
95 : : ValentChannel *channel)
96 : : {
97 : 10 : ValentChannelServicePrivate *priv = valent_channel_service_get_instance_private (self);
98 : 10 : ValentChannel *current = NULL;
99 : 20 : g_autoptr (GTlsCertificate) peer_certificate = NULL;
100 : 10 : const char *peer_certificate_cn = NULL;
101 : 10 : JsonNode *peer_identity = NULL;
102 : 10 : const char *device_id = NULL;
103 : 10 : const char *device_name = NULL;
104 : :
105 [ - + ]: 10 : g_assert (VALENT_IS_CHANNEL_SERVICE (self));
106 [ + - ]: 10 : g_assert (VALENT_IS_CHANNEL (channel));
107 : :
108 : : /* Ignore channels with an invalid deviceId or deviceName
109 : : */
110 : 10 : peer_identity = valent_channel_get_peer_identity (channel);
111 [ + - - + ]: 20 : if (!valent_packet_get_string (peer_identity, "deviceId", &device_id) ||
112 : 10 : !valent_device_validate_id (device_id))
113 : : {
114 : 0 : g_warning ("%s(): invalid device ID \"%s\"", G_STRFUNC, device_id);
115 : 0 : return FALSE;
116 : : }
117 : :
118 [ - + ]: 10 : if (!valent_packet_get_string (peer_identity, "deviceName", &device_name))
119 : : {
120 : 0 : g_warning ("%s(): expected \"deviceName\" field holding a string",
121 : : G_STRFUNC);
122 : 0 : return FALSE;
123 : : }
124 : :
125 [ - + ]: 10 : if (!valent_device_validate_name (device_name))
126 : : {
127 : 0 : g_warning ("%s(): invalid device name \"%s\"", G_STRFUNC, device_name);
128 : : }
129 : :
130 : : /* Ensure the deviceId field matches the certificate common name
131 : : */
132 : 10 : peer_certificate = valent_channel_ref_peer_certificate (channel);
133 : 10 : peer_certificate_cn = valent_certificate_get_common_name (peer_certificate);
134 [ - + ]: 10 : if (g_strcmp0 (device_id, peer_certificate_cn) != 0)
135 : : {
136 : 0 : g_warning ("%s(): device ID does not match certificate common name",
137 : : G_STRFUNC);
138 : 0 : return FALSE;
139 : : }
140 : :
141 : : /* Ensure the certificate matches that of any existing channel
142 : : */
143 : 10 : current = g_hash_table_lookup (priv->channels, device_id);
144 [ - + ]: 10 : if (current != NULL)
145 : : {
146 [ - + ]: 10 : g_autoptr (GTlsCertificate) certificate = NULL;
147 : :
148 : 0 : certificate = valent_channel_ref_peer_certificate (VALENT_CHANNEL (current));
149 [ # # ]: 0 : if (!g_tls_certificate_is_same (certificate, peer_certificate))
150 : : {
151 : 0 : g_warning ("%s(): existing channel with different certificate",
152 : : G_STRFUNC);
153 [ # # ]: 0 : return FALSE;
154 : : }
155 : : }
156 : :
157 : : /* Track the channel until destruction
158 : : */
159 [ - + ]: 10 : g_hash_table_replace (priv->channels,
160 : 10 : g_strdup (device_id),
161 : : g_object_ref (channel));
162 : 10 : g_signal_connect_object (channel,
163 : : "destroy",
164 : : G_CALLBACK (on_channel_destroyed),
165 : : self,
166 : : G_CONNECT_SWAPPED);
167 : :
168 : 10 : return TRUE;
169 : : }
170 : :
171 : : /*
172 : : * Identity Packet Helpers
173 : : */
174 : : static const char *
175 : 24 : get_chassis_type (void)
176 : : {
177 : 24 : static size_t guard = 0;
178 : 24 : static char *chassis = NULL;
179 : :
180 [ + + + - ]: 24 : if (g_once_init_enter (&guard))
181 : : {
182 : 7 : g_autoptr (GDBusConnection) connection = NULL;
183 [ + + ]: 7 : g_autoptr (GVariant) reply = NULL;
184 [ - + ]: 7 : g_autofree char *str = NULL;
185 : 7 : uint64_t type;
186 : :
187 : 7 : connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL);
188 : :
189 [ + + ]: 7 : if (connection == NULL)
190 : 5 : VALENT_GOTO (dmi_fallback);
191 : :
192 : 2 : reply = g_dbus_connection_call_sync (connection,
193 : : "org.freedesktop.hostname1",
194 : : "/org/freedesktop/hostname1",
195 : : "org.freedesktop.DBus.Properties",
196 : : "Get",
197 : : g_variant_new ("(ss)",
198 : : "org.freedesktop.hostname1",
199 : : "Chassis"),
200 : : G_VARIANT_TYPE ("(v)"),
201 : : G_DBUS_CALL_FLAGS_NONE,
202 : : -1,
203 : : NULL,
204 : : NULL);
205 : :
206 [ - + ]: 2 : if (reply != NULL)
207 : : {
208 : 0 : g_autoptr (GVariant) value = NULL;
209 : :
210 : 0 : g_variant_get (reply, "(v)", &value);
211 : 0 : g_variant_get (value, "s", &chassis);
212 : :
213 : : /* NOTE: "phone" is the KDE Connect deviceType */
214 [ # # ]: 0 : if (g_str_equal (chassis, "handset"))
215 : 0 : g_set_str (&chassis, "phone");
216 : :
217 [ # # ]: 0 : VALENT_GOTO (leave);
218 : : }
219 : :
220 : : /* Fallback to DMI. See the SMBIOS Specification 3.0 section 7.4.1:
221 : : * https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.0.0.pdf
222 : : */
223 : 2 : dmi_fallback:
224 [ + - + - ]: 14 : if (!g_file_get_contents ("/sys/class/dmi/id/chassis_type", &str, NULL, NULL) ||
225 : 7 : !g_ascii_string_to_unsigned (str, 10, 0, G_MAXUINT64, &type, NULL))
226 : 7 : type = 0x3;
227 : :
228 [ + - - - : 7 : switch (type)
- ]
229 : : {
230 : 7 : case 0x3: /* Desktop */
231 : : case 0x4: /* Low Profile Desktop */
232 : : case 0x6: /* Mini Tower */
233 : : case 0x7: /* Tower */
234 : 7 : g_set_str (&chassis, "desktop");
235 : 7 : break;
236 : :
237 : 0 : case 0x8: /* Portable */
238 : : case 0x9: /* Laptop */
239 : : case 0xA: /* Notebook */
240 : : case 0xE: /* Sub Notebook */
241 : 0 : g_set_str (&chassis, "laptop");
242 : 0 : break;
243 : :
244 : 0 : case 0xB: /* Hand Held */
245 : 0 : g_set_str (&chassis, "phone");
246 : 0 : break;
247 : :
248 : 0 : case 0x1E: /* Tablet */
249 : 0 : g_set_str (&chassis, "tablet");
250 : 0 : break;
251 : :
252 : 0 : default:
253 : 0 : g_set_str (&chassis, "desktop");
254 : : }
255 : :
256 : 7 : leave:
257 : 7 : g_once_init_leave (&guard, 1);
258 : : }
259 : :
260 : 24 : return chassis;
261 : : }
262 : :
263 : : /**
264 : : * collect_capabilities:
265 : : * @info: a `PeasPluginInfo`
266 : : * @incoming: a `GHashTable`
267 : : * @outgoing: a `GHashTable`
268 : : *
269 : : * Collect the capabilities from @info and add them to @incoming and @outgoing,
270 : : * using g_hash_table_add() to coalesce duplicates.
271 : : */
272 : : static inline void
273 : 130 : collect_capabilities (PeasPluginInfo *info,
274 : : GHashTable *incoming,
275 : : GHashTable *outgoing)
276 : : {
277 : 130 : const char *data = NULL;
278 : :
279 [ + + ]: 130 : if ((data = peas_plugin_info_get_external_data (info, "DevicePluginIncoming")) != NULL)
280 : : {
281 : 69 : g_autofree char **capabilities = NULL;
282 : :
283 : 69 : capabilities = g_strsplit (data, ";", -1);
284 : :
285 [ + + ]: 202 : for (unsigned int i = 0; capabilities[i] != NULL; i++)
286 : 133 : g_hash_table_add (incoming, g_steal_pointer (&capabilities[i]));
287 : : }
288 : :
289 [ + + ]: 130 : if ((data = peas_plugin_info_get_external_data (info, "DevicePluginOutgoing")) != NULL)
290 : : {
291 : 69 : g_autofree char **capabilities = NULL;
292 : :
293 : 69 : capabilities = g_strsplit (data, ";", -1);
294 : :
295 [ + + ]: 212 : for (unsigned int i = 0; capabilities[i] != NULL; i++)
296 : 143 : g_hash_table_add (outgoing, g_steal_pointer (&capabilities[i]));
297 : : }
298 : 130 : }
299 : :
300 : : static void
301 : 28 : on_device_name_changed (GSettings *settings,
302 : : const char *key_name,
303 : : ValentChannelService *self)
304 : : {
305 : 28 : ValentChannelServicePrivate *priv = valent_channel_service_get_instance_private (self);
306 : 28 : g_autofree char *name = NULL;
307 : :
308 [ - + ]: 28 : g_return_if_fail (VALENT_IS_CHANNEL_SERVICE (self));
309 : :
310 : 28 : name = g_settings_get_string (settings, "name");
311 [ + - + + ]: 28 : if (name == NULL || *name == '\0')
312 : : {
313 : 7 : g_auto (GStrv) hostname = NULL;
314 : :
315 : 7 : hostname = g_strsplit (g_get_host_name (), ".", 2);
316 : 7 : g_settings_set_string (settings, "name", hostname[0]);
317 : 7 : return;
318 : : }
319 : :
320 [ + - ]: 21 : if (g_set_str (&priv->name, name))
321 : : {
322 : 21 : valent_object_lock (VALENT_OBJECT (self));
323 [ - + ]: 21 : if (priv->identity)
324 : : {
325 : 0 : JsonObject *body = valent_packet_get_body (priv->identity);
326 : 0 : json_object_set_string_member (body, "deviceName", priv->name);
327 : 0 : g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_IDENTITY]);
328 : : }
329 : 21 : valent_object_unlock (VALENT_OBJECT (self));
330 : : }
331 : : }
332 : :
333 : : /* LCOV_EXCL_START */
334 : : static void
335 : : valent_channel_service_real_build_identity (ValentChannelService *service)
336 : : {
337 : : ValentChannelServicePrivate *priv = valent_channel_service_get_instance_private (service);
338 : : PeasEngine *engine;
339 : : g_autoptr (JsonBuilder) builder = NULL;
340 : : g_autoptr (GHashTable) incoming = NULL;
341 : : g_autoptr (GHashTable) outgoing = NULL;
342 : : GHashTableIter in_iter, out_iter;
343 : : const char *capability = NULL;
344 : : unsigned int n_plugins = 0;
345 : :
346 : : g_assert (VALENT_IS_CHANNEL_SERVICE (service));
347 : :
348 : : /* Filter the supported plugins and collect their capabilities */
349 : : incoming = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
350 : : outgoing = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
351 : :
352 : : engine = valent_get_plugin_engine ();
353 : : n_plugins = g_list_model_get_n_items (G_LIST_MODEL (engine));
354 : :
355 : : for (unsigned int i = 0; i < n_plugins; i++)
356 : : {
357 : : g_autoptr (PeasPluginInfo) info = NULL;
358 : :
359 : : info = g_list_model_get_item (G_LIST_MODEL (engine), i);
360 : : collect_capabilities (info, incoming, outgoing);
361 : : }
362 : :
363 : : /* Build the identity packet */
364 : : builder = json_builder_new ();
365 : : json_builder_begin_object (builder);
366 : :
367 : : /* Packet */
368 : : json_builder_set_member_name (builder, "id");
369 : : json_builder_add_int_value (builder, 0);
370 : : json_builder_set_member_name (builder, "type");
371 : : json_builder_add_string_value (builder, "kdeconnect.identity");
372 : :
373 : : /* Body */
374 : : json_builder_set_member_name (builder, "body");
375 : : json_builder_begin_object (builder);
376 : :
377 : : /* Metadata */
378 : : json_builder_set_member_name (builder, "deviceId");
379 : : json_builder_add_string_value (builder, priv->id);
380 : : json_builder_set_member_name (builder, "deviceName");
381 : : json_builder_add_string_value (builder, priv->name);
382 : : json_builder_set_member_name (builder, "deviceType");
383 : : json_builder_add_string_value (builder, get_chassis_type());
384 : : json_builder_set_member_name (builder, "protocolVersion");
385 : : json_builder_add_int_value (builder, VALENT_NETWORK_PROTOCOL_MAX);
386 : :
387 : : /* Incoming Capabilities */
388 : : json_builder_set_member_name (builder, "incomingCapabilities");
389 : : json_builder_begin_array (builder);
390 : :
391 : : g_hash_table_iter_init (&in_iter, incoming);
392 : :
393 : : while (g_hash_table_iter_next (&in_iter, (void **)&capability, NULL))
394 : : json_builder_add_string_value (builder, capability);
395 : :
396 : : json_builder_end_array (builder);
397 : :
398 : : /* Outgoing Capabilities */
399 : : json_builder_set_member_name (builder, "outgoingCapabilities");
400 : : json_builder_begin_array (builder);
401 : :
402 : : g_hash_table_iter_init (&out_iter, outgoing);
403 : :
404 : : while (g_hash_table_iter_next (&out_iter, (void **)&capability, NULL))
405 : : json_builder_add_string_value (builder, capability);
406 : :
407 : : json_builder_end_array (builder);
408 : :
409 : : /* End Body, Packet */
410 : : json_builder_end_object (builder);
411 : : json_builder_end_object (builder);
412 : :
413 : :
414 : : /* Store the identity */
415 : : valent_object_lock (VALENT_OBJECT (service));
416 : : g_clear_pointer (&priv->identity, json_node_unref);
417 : : priv->identity = json_builder_get_root (builder);
418 : : valent_object_unlock (VALENT_OBJECT (service));
419 : : }
420 : :
421 : : static void
422 : : valent_channel_service_real_channel (ValentChannelService *service,
423 : : ValentChannel *channel)
424 : : {
425 : : g_assert (VALENT_IS_CHANNEL_SERVICE (service));
426 : : g_assert (VALENT_IS_CHANNEL (channel));
427 : : }
428 : :
429 : : static void
430 : : valent_channel_service_real_identify (ValentChannelService *service,
431 : : const char *target)
432 : : {
433 : : g_assert (VALENT_IS_CHANNEL_SERVICE (service));
434 : : }
435 : : /* LCOV_EXCL_STOP */
436 : :
437 : : /*
438 : : * GObject
439 : : */
440 : : static void
441 : 21 : valent_channel_service_constructed (GObject *object)
442 : : {
443 : 21 : ValentChannelService *self = VALENT_CHANNEL_SERVICE (object);
444 : 21 : ValentChannelServicePrivate *priv = valent_channel_service_get_instance_private (self);
445 : :
446 : 21 : G_OBJECT_CLASS (valent_channel_service_parent_class)->constructed (object);
447 : :
448 : 21 : valent_object_lock (VALENT_OBJECT (self));
449 [ + - ]: 21 : if (priv->certificate == NULL)
450 : : {
451 : 42 : g_autoptr (ValentContext) context = NULL;
452 [ + - ]: 21 : g_autoptr (GFile) config = NULL;
453 : :
454 : 21 : context = valent_context_new (NULL, NULL, NULL);
455 : 21 : config = valent_context_get_config_file (context, ".");
456 [ + - ]: 21 : priv->certificate = valent_certificate_new_sync (g_file_peek_path (config),
457 : : NULL);
458 : : }
459 : :
460 : 21 : priv->id = valent_certificate_get_common_name (priv->certificate);
461 : 21 : priv->settings = g_settings_new ("ca.andyholmes.Valent");
462 : 21 : g_signal_connect_object (priv->settings,
463 : : "changed::name",
464 : : G_CALLBACK (on_device_name_changed),
465 : : self,
466 : : G_CONNECT_DEFAULT);
467 : 21 : on_device_name_changed (priv->settings, NULL, self);
468 : 21 : valent_object_unlock (VALENT_OBJECT (self));
469 : :
470 : 21 : valent_channel_service_build_identity (self);
471 : 21 : }
472 : :
473 : : static void
474 : 21 : valent_channel_service_finalize (GObject *object)
475 : : {
476 : 21 : ValentChannelService *self = VALENT_CHANNEL_SERVICE (object);
477 : 21 : ValentChannelServicePrivate *priv = valent_channel_service_get_instance_private (self);
478 : :
479 : 21 : valent_object_lock (VALENT_OBJECT (self));
480 [ + - ]: 21 : g_clear_object (&priv->certificate);
481 [ + - ]: 21 : g_clear_pointer (&priv->identity, json_node_unref);
482 [ + - ]: 21 : g_clear_pointer (&priv->name, g_free);
483 [ + - ]: 21 : g_clear_object (&priv->settings);
484 [ + - ]: 21 : g_clear_pointer (&priv->channels, g_hash_table_unref);
485 : 21 : valent_object_unlock (VALENT_OBJECT (self));
486 : :
487 : 21 : G_OBJECT_CLASS (valent_channel_service_parent_class)->finalize (object);
488 : 21 : }
489 : :
490 : : static void
491 : 6 : valent_channel_service_get_property (GObject *object,
492 : : guint prop_id,
493 : : GValue *value,
494 : : GParamSpec *pspec)
495 : : {
496 : 6 : ValentChannelService *self = VALENT_CHANNEL_SERVICE (object);
497 : 6 : ValentChannelServicePrivate *priv = valent_channel_service_get_instance_private (self);
498 : :
499 [ + + + - ]: 6 : switch ((ValentChannelServiceProperty)prop_id)
500 : : {
501 : : case PROP_CERTIFICATE:
502 : 1 : valent_object_lock (VALENT_OBJECT (self));
503 : 1 : g_value_set_object (value, priv->certificate);
504 : 1 : valent_object_unlock (VALENT_OBJECT (self));
505 : 1 : break;
506 : :
507 : : case PROP_ID:
508 : 1 : valent_object_lock (VALENT_OBJECT (self));
509 : 1 : g_value_set_string (value, priv->id);
510 : 1 : valent_object_unlock (VALENT_OBJECT (self));
511 : 1 : break;
512 : :
513 : : case PROP_IDENTITY:
514 : 4 : valent_object_lock (VALENT_OBJECT (self));
515 : 4 : g_value_set_boxed (value, priv->identity);
516 : 4 : valent_object_unlock (VALENT_OBJECT (self));
517 : 4 : break;
518 : :
519 : 0 : default:
520 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
521 : : }
522 : 6 : }
523 : :
524 : : static void
525 : 21 : valent_channel_service_set_property (GObject *object,
526 : : guint prop_id,
527 : : const GValue *value,
528 : : GParamSpec *pspec)
529 : : {
530 : 21 : ValentChannelService *self = VALENT_CHANNEL_SERVICE (object);
531 : 21 : ValentChannelServicePrivate *priv = valent_channel_service_get_instance_private (self);
532 : :
533 [ + - ]: 21 : switch ((ValentChannelServiceProperty)prop_id)
534 : : {
535 : : case PROP_CERTIFICATE:
536 : 21 : valent_object_lock (VALENT_OBJECT (self));
537 : 21 : priv->certificate = g_value_dup_object (value);
538 : 21 : valent_object_unlock (VALENT_OBJECT (self));
539 : 21 : break;
540 : :
541 : 0 : case PROP_ID:
542 : : case PROP_IDENTITY:
543 : : default:
544 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
545 : : }
546 : 21 : }
547 : :
548 : : static void
549 : 52 : valent_channel_service_class_init (ValentChannelServiceClass *klass)
550 : : {
551 : 52 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
552 : 52 : ValentChannelServiceClass *service_class = VALENT_CHANNEL_SERVICE_CLASS (klass);
553 : :
554 : 52 : object_class->constructed = valent_channel_service_constructed;
555 : 52 : object_class->finalize = valent_channel_service_finalize;
556 : 52 : object_class->get_property = valent_channel_service_get_property;
557 : 52 : object_class->set_property = valent_channel_service_set_property;
558 : :
559 : 52 : service_class->build_identity = valent_channel_service_real_build_identity;
560 : 52 : service_class->channel = valent_channel_service_real_channel;
561 : 52 : service_class->identify = valent_channel_service_real_identify;
562 : :
563 : : /**
564 : : * ValentChannelService:certificate: (getter ref_certificate)
565 : : *
566 : : * The TLS certificate the service uses to authenticate with other devices.
567 : : */
568 : 104 : properties [PROP_CERTIFICATE] =
569 : 52 : g_param_spec_object ("certificate", NULL, NULL,
570 : : G_TYPE_TLS_CERTIFICATE,
571 : : (G_PARAM_READWRITE |
572 : : G_PARAM_CONSTRUCT_ONLY |
573 : : G_PARAM_EXPLICIT_NOTIFY |
574 : : G_PARAM_STATIC_STRINGS));
575 : :
576 : : /**
577 : : * ValentChannelService:id: (getter dup_id)
578 : : *
579 : : * The local ID.
580 : : *
581 : : * This is the ID used to identify the local device, which should be unique
582 : : * among devices in a given network.
583 : : *
584 : : * This property is thread-safe. Emissions of [signal@GObject.Object::notify]
585 : : * are guaranteed to happen in the main thread.
586 : : *
587 : : * Since: 1.0
588 : : */
589 : 104 : properties [PROP_ID] =
590 : 52 : g_param_spec_string ("id", NULL, NULL,
591 : : NULL,
592 : : (G_PARAM_READABLE |
593 : : G_PARAM_EXPLICIT_NOTIFY |
594 : : G_PARAM_STATIC_STRINGS));
595 : :
596 : : /**
597 : : * ValentChannelService:identity: (getter ref_identity)
598 : : *
599 : : * The local identity packet.
600 : : *
601 : : * This is the identity packet sent by the [class@Valent.ChannelService]
602 : : * implementation to describe the local device.
603 : : *
604 : : * This property is thread-safe. Emissions of [signal@GObject.Object::notify]
605 : : * are guaranteed to happen in the main thread.
606 : : *
607 : : * Since: 1.0
608 : : */
609 : 104 : properties [PROP_IDENTITY] =
610 : 52 : g_param_spec_boxed ("identity", NULL, NULL,
611 : : JSON_TYPE_NODE,
612 : : (G_PARAM_READABLE |
613 : : G_PARAM_EXPLICIT_NOTIFY |
614 : : G_PARAM_STATIC_STRINGS));
615 : :
616 : 52 : g_object_class_install_properties (object_class, G_N_ELEMENTS (properties), properties);
617 : :
618 : : /**
619 : : * ValentChannelService::channel:
620 : : * @service: a `ValentChannelService`
621 : : * @channel: a `ValentChannel`
622 : : *
623 : : * Emitted when a new channel has been negotiated.
624 : : *
625 : : * In practice, when this is emitted a [class@Valent.DeviceManager] will
626 : : * ensure a [class@Valent.Device] exists to take ownership of @channel.
627 : : *
628 : : * Since: 1.0
629 : : */
630 : 104 : signals [CHANNEL] =
631 : 52 : g_signal_new ("channel",
632 : : G_TYPE_FROM_CLASS (klass),
633 : : G_SIGNAL_RUN_LAST,
634 : : G_STRUCT_OFFSET (ValentChannelServiceClass, channel),
635 : : NULL, NULL,
636 : : g_cclosure_marshal_VOID__OBJECT,
637 : : G_TYPE_NONE, 1, VALENT_TYPE_CHANNEL);
638 : 52 : g_signal_set_va_marshaller (signals [CHANNEL],
639 : : G_TYPE_FROM_CLASS (klass),
640 : : g_cclosure_marshal_VOID__OBJECTv);
641 : 52 : }
642 : :
643 : : static void
644 : 21 : valent_channel_service_init (ValentChannelService *self)
645 : : {
646 : 21 : ValentChannelServicePrivate *priv = valent_channel_service_get_instance_private (self);
647 : :
648 : 21 : priv->channels = g_hash_table_new_full (g_str_hash,
649 : : g_str_equal,
650 : : g_free,
651 : : _valent_object_deref);
652 : 21 : }
653 : :
654 : : /**
655 : : * valent_channel_service_ref_certificate: (get-property certificate)
656 : : * @self: a `ValentChannelService`
657 : : *
658 : : * Get the TLS certificate for the service.
659 : : *
660 : : * Returns: (transfer full) (not nullable): the service TLS certificate
661 : : */
662 : : GTlsCertificate *
663 : 10 : valent_channel_service_ref_certificate (ValentChannelService *service)
664 : : {
665 : 10 : ValentChannelServicePrivate *priv = valent_channel_service_get_instance_private (service);
666 : 10 : GTlsCertificate *ret = NULL;
667 : :
668 [ - + ]: 10 : g_return_val_if_fail (VALENT_IS_CHANNEL_SERVICE (service), NULL);
669 : :
670 : 10 : valent_object_lock (VALENT_OBJECT (service));
671 : 10 : ret = g_object_ref (priv->certificate);
672 : 10 : valent_object_unlock (VALENT_OBJECT (service));
673 : :
674 : 10 : return g_steal_pointer (&ret);
675 : : }
676 : :
677 : : /**
678 : : * valent_channel_service_dup_id: (get-property id)
679 : : * @service: a `ValentChannelService`
680 : : *
681 : : * Get the local ID.
682 : : *
683 : : * Returns: (transfer full) (not nullable): the service ID
684 : : *
685 : : * Since: 1.0
686 : : */
687 : : char *
688 : 9 : valent_channel_service_dup_id (ValentChannelService *service)
689 : : {
690 : 9 : ValentChannelServicePrivate *priv = valent_channel_service_get_instance_private (service);
691 : 9 : char *ret;
692 : :
693 [ - + ]: 9 : g_return_val_if_fail (VALENT_IS_CHANNEL_SERVICE (service), NULL);
694 : :
695 : 9 : valent_object_lock (VALENT_OBJECT (service));
696 [ - + ]: 9 : ret = g_strdup (priv->id);
697 : 9 : valent_object_unlock (VALENT_OBJECT (service));
698 : :
699 : 9 : return g_steal_pointer (&ret);
700 : : }
701 : :
702 : : /**
703 : : * valent_channel_service_ref_identity: (get-property identity)
704 : : * @service: a `ValentChannelService`
705 : : *
706 : : * Get the local identity packet.
707 : : *
708 : : * Returns: (transfer full): a KDE Connect packet
709 : : *
710 : : * Since: 1.0
711 : : */
712 : : JsonNode *
713 : 27 : valent_channel_service_ref_identity (ValentChannelService *service)
714 : : {
715 : 27 : ValentChannelServicePrivate *priv = valent_channel_service_get_instance_private (service);
716 : 27 : JsonNode *ret;
717 : :
718 [ - + ]: 27 : g_return_val_if_fail (VALENT_IS_CHANNEL_SERVICE (service), NULL);
719 : :
720 : 27 : valent_object_lock (VALENT_OBJECT (service));
721 : 27 : ret = json_node_ref (priv->identity);
722 : 27 : valent_object_unlock (VALENT_OBJECT (service));
723 : :
724 : 27 : return g_steal_pointer (&ret);
725 : : }
726 : :
727 : : /**
728 : : * valent_channel_service_build_identity: (virtual build_identity)
729 : : * @service: a `ValentChannelService`
730 : : *
731 : : * Rebuild the local KDE Connect identity packet.
732 : : *
733 : : * This method is called to rebuild the identity packet used to identify the
734 : : * host device to remote devices.
735 : : *
736 : : * Implementations that override [vfunc@Valent.ChannelService.build_identity]
737 : : * should chain-up first, then call [method@Valent.ChannelService.ref_identity]
738 : : * and modify that.
739 : : *
740 : : * Since: 1.0
741 : : */
742 : : void
743 : 24 : valent_channel_service_build_identity (ValentChannelService *service)
744 : : {
745 : 24 : VALENT_ENTRY;
746 : :
747 [ - + ]: 24 : g_return_if_fail (VALENT_IS_CHANNEL_SERVICE (service));
748 : :
749 : 24 : valent_object_lock (VALENT_OBJECT (service));
750 : 24 : VALENT_CHANNEL_SERVICE_GET_CLASS (service)->build_identity (service);
751 : 24 : valent_object_unlock (VALENT_OBJECT (service));
752 : :
753 : 24 : VALENT_EXIT;
754 : : }
755 : :
756 : : /**
757 : : * valent_channel_service_identify: (virtual identify)
758 : : * @service: a `ValentChannelService`
759 : : * @target: (nullable): a target string
760 : : *
761 : : * Identify the host device to the network.
762 : : *
763 : : * This method is called to announce the availability of the host device to
764 : : * other devices.
765 : : *
766 : : * Implementations that override [vfunc@Valent.ChannelService.identify] may
767 : : * ignore @target or use it to address a particular device.
768 : : *
769 : : * Since: 1.0
770 : : */
771 : : void
772 : 8 : valent_channel_service_identify (ValentChannelService *service,
773 : : const char *target)
774 : : {
775 : 8 : VALENT_ENTRY;
776 : :
777 [ - + ]: 8 : g_return_if_fail (VALENT_IS_CHANNEL_SERVICE (service));
778 : :
779 : 8 : VALENT_CHANNEL_SERVICE_GET_CLASS (service)->identify (service, target);
780 : :
781 : 8 : VALENT_EXIT;
782 : : }
783 : :
784 : : /**
785 : : * valent_channel_service_channel:
786 : : * @service: a `ValentChannelService`
787 : : * @channel: a `ValentChannel`
788 : : *
789 : : * Emit [signal@Valent.ChannelService::channel] on @service.
790 : : *
791 : : * If @channel fails basic verification checks (e.g. certificate errors), the
792 : : * signal will not be emitted and [method@Valent.Object.destroy] will be called
793 : : * on @channel.
794 : : *
795 : : * This method should only be called by implementations of
796 : : * [class@Valent.ChannelService].
797 : : *
798 : : * Since: 1.0
799 : : */
800 : : void
801 : 10 : valent_channel_service_channel (ValentChannelService *service,
802 : : ValentChannel *channel)
803 : : {
804 [ - + ]: 10 : g_return_if_fail (VALENT_IS_CHANNEL_SERVICE (service));
805 [ + - ]: 10 : g_return_if_fail (VALENT_IS_CHANNEL (channel));
806 : :
807 [ + - ]: 10 : if (valent_channel_service_verify_channel (service, channel))
808 : 10 : g_signal_emit (G_OBJECT (service), signals [CHANNEL], 0, channel);
809 [ # # ]: 0 : else if (!valent_object_in_destruction (VALENT_OBJECT (channel)))
810 : 0 : valent_object_destroy (VALENT_OBJECT (channel));
811 : : }
812 : :
|