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