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