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