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