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"
5 : :
6 : : #include "config.h"
7 : :
8 : : #include <time.h>
9 : :
10 : : #include <gio/gio.h>
11 : : #include <json-glib/json-glib.h>
12 : : #include <libvalent-core.h>
13 : :
14 : : #include "valent-channel.h"
15 : : #include "valent-packet.h"
16 : :
17 : :
18 : : /**
19 : : * ValentChannel:
20 : : *
21 : : * A base class for device connections.
22 : : *
23 : : * `ValentChannel` is a base class for the primary communication channel in
24 : : * Valent. It is effectively an abstraction layer around a [class@Gio.IOStream].
25 : : *
26 : : * ## Packet Exchange
27 : : *
28 : : * The core of the KDE Connect protocol is built on the exchange of JSON
29 : : * packets, similar to JSON-RPC. Packets can be queued concurrently from
30 : : * different threads with [method@Valent.Channel.write_packet] and read
31 : : * sequentially with [method@Valent.Channel.read_packet].
32 : : *
33 : : * Packets may contain payload information, allowing devices to negotiate
34 : : * auxiliary connections. Incoming connections can be accepted by passing the
35 : : * packet to [method@Valent.Channel.download], or opened by passing the packet
36 : : * to [method@Valent.Channel.upload].
37 : : *
38 : : * ## Implementation Notes
39 : : *
40 : : * Implementations should override [vfunc@Valent.Channel.download] and
41 : : * [vfunc@Valent.Channel.upload] to support accepting and opening auxiliary
42 : : * connections, respectively. If pairing involves exchanging a key, override
43 : : * [vfunc@Valent.Channel.get_verification_key]. To know when to store persistent
44 : : * data related to the connection, override [vfunc@Valent.Channel.store_data].
45 : : *
46 : : * Since: 1.0
47 : : */
48 : :
49 : : typedef struct
50 : : {
51 : : GIOStream *base_stream;
52 : : JsonNode *identity;
53 : : JsonNode *peer_identity;
54 : :
55 : : /* Packet Buffer */
56 : : GDataInputStream *input_buffer;
57 : : GMainLoop *output_buffer;
58 : : } ValentChannelPrivate;
59 : :
60 [ + + + - ]: 6196 : G_DEFINE_TYPE_WITH_PRIVATE (ValentChannel, valent_channel, VALENT_TYPE_OBJECT)
61 : :
62 : : enum {
63 : : PROP_0,
64 : : PROP_BASE_STREAM,
65 : : PROP_IDENTITY,
66 : : PROP_PEER_IDENTITY,
67 : : N_PROPERTIES
68 : : };
69 : :
70 : : static GParamSpec *properties[N_PROPERTIES] = { NULL, };
71 : :
72 : :
73 : : /* LCOV_EXCL_START */
74 : : static const char *
75 : : valent_channel_real_get_verification_key (ValentChannel *channel)
76 : : {
77 : : return NULL;
78 : : }
79 : :
80 : : static GIOStream *
81 : : valent_channel_real_download (ValentChannel *channel,
82 : : JsonNode *packet,
83 : : GCancellable *cancellable,
84 : : GError **error)
85 : : {
86 : : g_set_error (error,
87 : : G_IO_ERROR,
88 : : G_IO_ERROR_NOT_SUPPORTED,
89 : : "%s does not implement download()",
90 : : G_OBJECT_TYPE_NAME (channel));
91 : : return NULL;
92 : : }
93 : :
94 : : static void
95 : : valent_channel_real_download_task (GTask *task,
96 : : gpointer source_object,
97 : : gpointer task_data,
98 : : GCancellable *cancellable)
99 : : {
100 : : ValentChannel *self = source_object;
101 : : JsonNode *packet = task_data;
102 : : g_autoptr (GIOStream) stream = NULL;
103 : : GError *error = NULL;
104 : :
105 : : if (g_task_return_error_if_cancelled (task))
106 : : return;
107 : :
108 : : stream = VALENT_CHANNEL_GET_CLASS (self)->download (self,
109 : : packet,
110 : : cancellable,
111 : : &error);
112 : :
113 : : if (stream == NULL)
114 : : return g_task_return_error (task, error);
115 : :
116 : : g_task_return_pointer (task, g_steal_pointer (&stream), g_object_unref);
117 : : }
118 : :
119 : : static void
120 : : valent_channel_real_download_async (ValentChannel *channel,
121 : : JsonNode *packet,
122 : : GCancellable *cancellable,
123 : : GAsyncReadyCallback callback,
124 : : gpointer user_data)
125 : : {
126 : : g_autoptr (GTask) task = NULL;
127 : :
128 : : g_assert (VALENT_IS_CHANNEL (channel));
129 : : g_assert (VALENT_IS_PACKET (packet));
130 : : g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
131 : :
132 : : task = g_task_new (channel, cancellable, callback, user_data);
133 : : g_task_set_source_tag (task, valent_channel_real_download_async);
134 : : g_task_set_task_data (task,
135 : : json_node_ref (packet),
136 : : (GDestroyNotify)json_node_unref);
137 : : g_task_run_in_thread (task, valent_channel_real_download_task);
138 : : }
139 : :
140 : : static GIOStream *
141 : : valent_channel_real_download_finish (ValentChannel *channel,
142 : : GAsyncResult *result,
143 : : GError **error)
144 : : {
145 : : g_assert (VALENT_IS_CHANNEL (channel));
146 : : g_assert (g_task_is_valid (result, channel));
147 : : g_assert (error == NULL || *error == NULL);
148 : :
149 : : return g_task_propagate_pointer (G_TASK (result), error);
150 : : }
151 : :
152 : : static GIOStream *
153 : : valent_channel_real_upload (ValentChannel *channel,
154 : : JsonNode *packet,
155 : : GCancellable *cancellable,
156 : : GError **error)
157 : : {
158 : : g_set_error (error,
159 : : G_IO_ERROR,
160 : : G_IO_ERROR_NOT_SUPPORTED,
161 : : "%s does not implement upload()",
162 : : G_OBJECT_TYPE_NAME (channel));
163 : : return NULL;
164 : : }
165 : :
166 : : static void
167 : : valent_channel_upload_task (GTask *task,
168 : : gpointer source_object,
169 : : gpointer task_data,
170 : : GCancellable *cancellable)
171 : : {
172 : : ValentChannel *self = source_object;
173 : : JsonNode *packet = task_data;
174 : : g_autoptr (GIOStream) stream = NULL;
175 : : GError *error = NULL;
176 : :
177 : : if (g_task_return_error_if_cancelled (task))
178 : : return;
179 : :
180 : : stream = VALENT_CHANNEL_GET_CLASS (self)->upload (self,
181 : : packet,
182 : : cancellable,
183 : : &error);
184 : :
185 : : if (stream == NULL)
186 : : return g_task_return_error (task, error);
187 : :
188 : : g_task_return_pointer (task, g_steal_pointer (&stream), g_object_unref);
189 : : }
190 : :
191 : : static void
192 : : valent_channel_real_upload_async (ValentChannel *channel,
193 : : JsonNode *packet,
194 : : GCancellable *cancellable,
195 : : GAsyncReadyCallback callback,
196 : : gpointer user_data)
197 : : {
198 : : g_autoptr (GTask) task = NULL;
199 : :
200 : : g_assert (VALENT_IS_CHANNEL (channel));
201 : : g_assert (VALENT_IS_PACKET (packet));
202 : : g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
203 : :
204 : : task = g_task_new (channel, cancellable, callback, user_data);
205 : : g_task_set_source_tag (task, valent_channel_real_upload_async);
206 : : g_task_set_task_data (task,
207 : : json_node_ref (packet),
208 : : (GDestroyNotify)json_node_unref);
209 : : g_task_run_in_thread (task, valent_channel_upload_task);
210 : : }
211 : :
212 : : static GIOStream *
213 : : valent_channel_real_upload_finish (ValentChannel *channel,
214 : : GAsyncResult *result,
215 : : GError **error)
216 : : {
217 : : g_assert (VALENT_IS_CHANNEL (channel));
218 : : g_assert (g_task_is_valid (result, channel));
219 : : g_assert (error == NULL || *error == NULL);
220 : :
221 : : return g_task_propagate_pointer (G_TASK (result), error);
222 : : }
223 : :
224 : : static void
225 : : valent_channel_real_store_data (ValentChannel *channel,
226 : : ValentContext *context)
227 : : {
228 : : g_assert (VALENT_IS_CHANNEL (channel));
229 : : g_assert (VALENT_IS_CONTEXT (context));
230 : : }
231 : : /* LCOV_EXCL_STOP */
232 : :
233 : :
234 : : /*
235 : : * ValentChannel
236 : : */
237 : : static inline gboolean
238 : 940 : valent_channel_return_error_if_closed (ValentChannel *self,
239 : : GTask *task)
240 : : {
241 : 940 : ValentChannelPrivate *priv = valent_channel_get_instance_private (self);
242 : :
243 [ - + ]: 940 : if (g_task_return_error_if_cancelled (task))
244 : : return TRUE;
245 : :
246 : 940 : valent_object_lock (VALENT_OBJECT (self));
247 [ + - + + ]: 940 : if (priv->base_stream == NULL || g_io_stream_is_closed (priv->base_stream))
248 : : {
249 [ - + ]: 7 : if (priv->output_buffer != NULL)
250 : 0 : g_main_loop_quit (priv->output_buffer);
251 [ - + ]: 7 : g_clear_pointer (&priv->output_buffer, g_main_loop_unref);
252 [ - + ]: 7 : g_clear_object (&priv->input_buffer);
253 : 7 : valent_object_unlock (VALENT_OBJECT (self));
254 : :
255 : 7 : g_task_return_new_error (task,
256 : : G_IO_ERROR,
257 : : G_IO_ERROR_CONNECTION_CLOSED,
258 : : "Channel is closed");
259 : 7 : return TRUE;
260 : : }
261 : :
262 : : return FALSE;
263 : : }
264 : :
265 : : static gpointer
266 : 206 : valent_channel_write_packet_worker (gpointer data)
267 : : {
268 : 397 : g_autoptr (GMainLoop) loop = (GMainLoop *)data;
269 : 206 : GMainContext *context = g_main_loop_get_context (loop);
270 : :
271 : : /* The loop quits when the channel is closed, then the context is drained to
272 : : * ensure all tasks return. */
273 : 206 : g_main_context_push_thread_default (context);
274 : :
275 : 206 : g_main_loop_run (loop);
276 : :
277 [ - + ]: 206 : while (g_main_context_pending (context))
278 : 0 : g_main_context_iteration (NULL, FALSE);
279 : :
280 : 191 : g_main_context_pop_thread_default (context);
281 : :
282 [ + - ]: 191 : return NULL;
283 : : }
284 : :
285 : : static void
286 : 206 : valent_channel_set_base_stream (ValentChannel *self,
287 : : GIOStream *base_stream)
288 : : {
289 : 206 : ValentChannelPrivate *priv = valent_channel_get_instance_private (self);
290 : :
291 [ + - ]: 206 : g_assert (VALENT_IS_CHANNEL (self));
292 : :
293 [ + - ]: 206 : if (base_stream != NULL)
294 : : {
295 : 412 : g_autoptr (GMainContext) context = NULL;
296 : 206 : GInputStream *input_stream;
297 : 206 : GThread *thread;
298 : :
299 : 206 : valent_object_lock (VALENT_OBJECT (self));
300 : 206 : input_stream = g_io_stream_get_input_stream (base_stream);
301 : :
302 : 206 : priv->base_stream = g_object_ref (base_stream);
303 : 206 : priv->input_buffer = g_object_new (G_TYPE_DATA_INPUT_STREAM,
304 : : "base-stream", input_stream,
305 : : "close-base-stream", FALSE,
306 : : NULL);
307 : :
308 : 206 : context = g_main_context_new ();
309 : 206 : priv->output_buffer = g_main_loop_new (context, FALSE);
310 : 206 : thread = g_thread_new ("valent-channel",
311 : : valent_channel_write_packet_worker,
312 : 206 : g_main_loop_ref (priv->output_buffer));
313 [ + - ]: 206 : g_clear_pointer (&thread, g_thread_unref);
314 [ + - ]: 206 : valent_object_unlock (VALENT_OBJECT (self));
315 : : }
316 : 206 : }
317 : :
318 : :
319 : : /*
320 : : * GObject
321 : : */
322 : : static void
323 : 205 : valent_channel_finalize (GObject *object)
324 : : {
325 : 205 : ValentChannel *self = VALENT_CHANNEL (object);
326 : 205 : ValentChannelPrivate *priv = valent_channel_get_instance_private (self);
327 : :
328 : 205 : valent_object_lock (VALENT_OBJECT (self));
329 [ + + ]: 205 : g_clear_pointer (&priv->output_buffer, g_main_loop_unref);
330 [ + + ]: 205 : g_clear_object (&priv->input_buffer);
331 [ + - ]: 205 : g_clear_object (&priv->base_stream);
332 [ + - ]: 205 : g_clear_pointer (&priv->identity, json_node_unref);
333 [ + - ]: 205 : g_clear_pointer (&priv->peer_identity, json_node_unref);
334 : 205 : valent_object_unlock (VALENT_OBJECT (self));
335 : :
336 : 205 : G_OBJECT_CLASS (valent_channel_parent_class)->finalize (object);
337 : 205 : }
338 : :
339 : : static void
340 : 3 : valent_channel_get_property (GObject *object,
341 : : guint prop_id,
342 : : GValue *value,
343 : : GParamSpec *pspec)
344 : : {
345 : 3 : ValentChannel *self = VALENT_CHANNEL (object);
346 : 3 : ValentChannelPrivate *priv = valent_channel_get_instance_private (self);
347 : :
348 [ + + + - ]: 3 : switch (prop_id)
349 : : {
350 : 1 : case PROP_BASE_STREAM:
351 : 1 : g_value_take_object (value, valent_channel_ref_base_stream (self));
352 : 1 : break;
353 : :
354 : 1 : case PROP_IDENTITY:
355 : 1 : g_value_set_boxed (value, priv->identity);
356 : 1 : break;
357 : :
358 : 1 : case PROP_PEER_IDENTITY:
359 : 1 : g_value_set_boxed (value, priv->peer_identity);
360 : 1 : break;
361 : :
362 : 0 : default:
363 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
364 : : }
365 : 3 : }
366 : :
367 : : static void
368 : 618 : valent_channel_set_property (GObject *object,
369 : : guint prop_id,
370 : : const GValue *value,
371 : : GParamSpec *pspec)
372 : : {
373 : 618 : ValentChannel *self = VALENT_CHANNEL (object);
374 : 618 : ValentChannelPrivate *priv = valent_channel_get_instance_private (self);
375 : :
376 [ + + + - ]: 618 : switch (prop_id)
377 : : {
378 : 206 : case PROP_BASE_STREAM:
379 : 206 : valent_channel_set_base_stream (self, g_value_get_object (value));
380 : 206 : break;
381 : :
382 : 206 : case PROP_IDENTITY:
383 : 206 : priv->identity = g_value_dup_boxed (value);
384 : 206 : break;
385 : :
386 : 206 : case PROP_PEER_IDENTITY:
387 : 206 : priv->peer_identity = g_value_dup_boxed (value);
388 : 206 : break;
389 : :
390 : 0 : default:
391 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
392 : : }
393 : 618 : }
394 : :
395 : : static void
396 : 31 : valent_channel_class_init (ValentChannelClass *klass)
397 : : {
398 : 31 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
399 : :
400 : 31 : object_class->finalize = valent_channel_finalize;
401 : 31 : object_class->get_property = valent_channel_get_property;
402 : 31 : object_class->set_property = valent_channel_set_property;
403 : :
404 : 31 : klass->get_verification_key = valent_channel_real_get_verification_key;
405 : 31 : klass->download = valent_channel_real_download;
406 : 31 : klass->download_async = valent_channel_real_download_async;
407 : 31 : klass->download_finish = valent_channel_real_download_finish;
408 : 31 : klass->upload = valent_channel_real_upload;
409 : 31 : klass->upload_async = valent_channel_real_upload_async;
410 : 31 : klass->upload_finish = valent_channel_real_upload_finish;
411 : 31 : klass->store_data = valent_channel_real_store_data;
412 : :
413 : : /**
414 : : * ValentChannel:base-stream: (getter ref_base_stream)
415 : : *
416 : : * The base [class@Gio.IOStream] for the channel.
417 : : *
418 : : * Implementations of [class@Valent.ChannelService] must set this property
419 : : * during construction.
420 : : *
421 : : * Since: 1.0
422 : : */
423 : 62 : properties [PROP_BASE_STREAM] =
424 : 31 : g_param_spec_object ("base-stream", NULL, NULL,
425 : : G_TYPE_IO_STREAM,
426 : : (G_PARAM_READWRITE |
427 : : G_PARAM_CONSTRUCT_ONLY |
428 : : G_PARAM_EXPLICIT_NOTIFY |
429 : : G_PARAM_STATIC_STRINGS));
430 : :
431 : : /**
432 : : * ValentChannel:identity: (getter get_identity)
433 : : *
434 : : * The local identity packet.
435 : : *
436 : : * This is the identity packet sent by the [class@Valent.ChannelService]
437 : : * implementation to identify the host system.
438 : : *
439 : : * Implementations of [class@Valent.ChannelService] must set this property
440 : : * during construction.
441 : : *
442 : : * Since: 1.0
443 : : */
444 : 62 : properties [PROP_IDENTITY] =
445 : 31 : g_param_spec_boxed ("identity", NULL, NULL,
446 : : JSON_TYPE_NODE,
447 : : (G_PARAM_READWRITE |
448 : : G_PARAM_CONSTRUCT_ONLY |
449 : : G_PARAM_EXPLICIT_NOTIFY |
450 : : G_PARAM_STATIC_STRINGS));
451 : :
452 : : /**
453 : : * ValentChannel:peer-identity: (getter get_peer_identity)
454 : : *
455 : : * The peer identity packet.
456 : : *
457 : : * This is the identity packet sent by the peer to identify itself.
458 : : *
459 : : * Implementations of [class@Valent.ChannelService] must set this property
460 : : * during construction.
461 : : *
462 : : * Since: 1.0
463 : : */
464 : 62 : properties [PROP_PEER_IDENTITY] =
465 : 31 : g_param_spec_boxed ("peer-identity", NULL, NULL,
466 : : JSON_TYPE_NODE,
467 : : (G_PARAM_READWRITE |
468 : : G_PARAM_CONSTRUCT_ONLY |
469 : : G_PARAM_EXPLICIT_NOTIFY |
470 : : G_PARAM_STATIC_STRINGS));
471 : :
472 : 31 : g_object_class_install_properties (object_class, N_PROPERTIES, properties);
473 : 31 : }
474 : :
475 : : static void
476 : 206 : valent_channel_init (ValentChannel *self)
477 : : {
478 : 206 : }
479 : :
480 : : /**
481 : : * valent_channel_ref_base_stream: (get-property base-stream)
482 : : * @channel: a `ValentChannel`
483 : : *
484 : : * Get the base [class@Gio.IOStream].
485 : : *
486 : : * Returns: (transfer full) (nullable): the base stream
487 : : *
488 : : * Since: 1.0
489 : : */
490 : : GIOStream *
491 : 18 : valent_channel_ref_base_stream (ValentChannel *channel)
492 : : {
493 : 18 : ValentChannelPrivate *priv = valent_channel_get_instance_private (channel);
494 : 18 : GIOStream *ret = NULL;
495 : :
496 [ + - ]: 18 : g_return_val_if_fail (VALENT_IS_CHANNEL (channel), NULL);
497 : :
498 : 18 : valent_object_lock (VALENT_OBJECT (channel));
499 [ + - ]: 18 : if (priv->base_stream != NULL)
500 : 18 : ret = g_object_ref (priv->base_stream);
501 : 18 : valent_object_unlock (VALENT_OBJECT (channel));
502 : :
503 : 18 : return g_steal_pointer (&ret);
504 : : }
505 : :
506 : : /**
507 : : * valent_channel_get_identity: (get-property identity)
508 : : * @channel: A `ValentChannel`
509 : : *
510 : : * Get the local identity packet.
511 : : *
512 : : * Returns: (transfer none): a KDE Connect packet
513 : : *
514 : : * Since: 1.0
515 : : */
516 : : JsonNode *
517 : 1 : valent_channel_get_identity (ValentChannel *channel)
518 : : {
519 : 1 : ValentChannelPrivate *priv = valent_channel_get_instance_private (channel);
520 : :
521 [ + - ]: 1 : g_return_val_if_fail (VALENT_IS_CHANNEL (channel), NULL);
522 : :
523 : 1 : return priv->identity;
524 : : }
525 : :
526 : : /**
527 : : * valent_channel_get_peer_identity: (get-property peer-identity)
528 : : * @channel: A `ValentChannel`
529 : : *
530 : : * Get the peer identity packet.
531 : : *
532 : : * Returns: (transfer none): a KDE Connect packet
533 : : *
534 : : * Since: 1.0
535 : : */
536 : : JsonNode *
537 : 83 : valent_channel_get_peer_identity (ValentChannel *channel)
538 : : {
539 : 83 : ValentChannelPrivate *priv = valent_channel_get_instance_private (channel);
540 : :
541 [ + - ]: 83 : g_return_val_if_fail (VALENT_IS_CHANNEL (channel), NULL);
542 : :
543 : 83 : return priv->peer_identity;
544 : : }
545 : :
546 : : /**
547 : : * valent_channel_get_verification_key: (virtual get_verification_key)
548 : : * @channel: a `ValentChannel`
549 : : *
550 : : * Get a verification key for the connection.
551 : : *
552 : : * Implementations that involve exchanging a key should return a string for the
553 : : * user to authenticate the connection, similar to a Bluetooth PIN.
554 : : *
555 : : * Returns: (transfer none): a verification key
556 : : *
557 : : * Since: 1.0
558 : : */
559 : : const char *
560 : 4 : valent_channel_get_verification_key (ValentChannel *channel)
561 : : {
562 : 4 : const char *ret;
563 : :
564 : 4 : VALENT_ENTRY;
565 : :
566 [ + - ]: 4 : g_return_val_if_fail (VALENT_IS_CHANNEL (channel), NULL);
567 : :
568 : 4 : ret = VALENT_CHANNEL_GET_CLASS (channel)->get_verification_key (channel);
569 : :
570 : 4 : VALENT_RETURN (ret);
571 : : }
572 : :
573 : : /**
574 : : * valent_channel_close:
575 : : * @channel: a `ValentChannel`
576 : : * @cancellable: (nullable): a `GCancellable`
577 : : * @error: (nullable): a `GError`
578 : : *
579 : : * Close the channel.
580 : : *
581 : : * Returns: %TRUE if successful, or %FALSE with @error set
582 : : */
583 : : gboolean
584 : 262 : valent_channel_close (ValentChannel *channel,
585 : : GCancellable *cancellable,
586 : : GError **error)
587 : : {
588 : 262 : ValentChannelPrivate *priv = valent_channel_get_instance_private (channel);
589 : 262 : gboolean ret = TRUE;
590 : :
591 : 262 : VALENT_ENTRY;
592 : :
593 [ + - ]: 262 : g_return_val_if_fail (VALENT_IS_CHANNEL (channel), FALSE);
594 [ - + - - : 262 : g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
- - - - ]
595 [ + + - + ]: 262 : g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
596 : :
597 : 262 : valent_object_lock (VALENT_OBJECT (channel));
598 [ - + + + ]: 262 : if (priv->base_stream != NULL && !g_io_stream_is_closed (priv->base_stream))
599 : : {
600 : 192 : ret = g_io_stream_close (priv->base_stream, cancellable, error);
601 : :
602 [ + - ]: 192 : if (priv->output_buffer != NULL)
603 : 192 : g_main_loop_quit (priv->output_buffer);
604 [ + - ]: 192 : g_clear_pointer (&priv->output_buffer, g_main_loop_unref);
605 [ - + ]: 192 : g_clear_object (&priv->input_buffer);
606 : : }
607 : 262 : valent_object_unlock (VALENT_OBJECT (channel));
608 : :
609 : 262 : VALENT_RETURN (ret);
610 : : }
611 : :
612 : : static void
613 : 78 : valent_channel_close_task (GTask *task,
614 : : gpointer source_object,
615 : : gpointer task_data,
616 : : GCancellable *cancellable)
617 : : {
618 : 78 : ValentChannel *self = source_object;
619 : 78 : GError *error = NULL;
620 : :
621 [ - + ]: 78 : if (g_task_return_error_if_cancelled (task))
622 : 0 : return;
623 : :
624 [ + - ]: 78 : if (valent_channel_close (self, cancellable, &error))
625 : 78 : g_task_return_boolean (task, TRUE);
626 : : else
627 : 0 : g_task_return_error (task, error);
628 : : }
629 : :
630 : : /**
631 : : * valent_channel_close_async:
632 : : * @channel: a `ValentChannel`
633 : : * @cancellable: (nullable): a `GCancellable`
634 : : * @callback: (scope async): a `GAsyncReadyCallback`
635 : : * @user_data: user supplied data
636 : : *
637 : : * Close the channel asynchronously.
638 : : *
639 : : * Call [method@Valent.Channel.close_finish] to get the result.
640 : : *
641 : : * Since: 1.0
642 : : */
643 : : void
644 : 78 : valent_channel_close_async (ValentChannel *channel,
645 : : GCancellable *cancellable,
646 : : GAsyncReadyCallback callback,
647 : : gpointer user_data)
648 : : {
649 : 156 : g_autoptr (GTask) task = NULL;
650 : :
651 : 78 : VALENT_ENTRY;
652 : :
653 [ + - ]: 78 : g_return_if_fail (VALENT_IS_CHANNEL (channel));
654 [ - + - - : 78 : g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- - - - ]
655 : :
656 : 78 : task = g_task_new (channel, cancellable, callback, user_data);
657 [ + - ]: 78 : g_task_set_source_tag (task, valent_channel_close_async);
658 : 78 : g_task_run_in_thread (task, valent_channel_close_task);
659 : :
660 [ + - ]: 78 : VALENT_EXIT;
661 : : }
662 : :
663 : : /**
664 : : * valent_channel_close_finish:
665 : : * @channel: a `ValentChannel`
666 : : * @result: a `GAsyncResult`
667 : : * @error: (nullable): a `GError`
668 : : *
669 : : * Finish an operation started by [method@Valent.Channel.close_async].
670 : : *
671 : : * Returns: %TRUE if successful, or %FALSE with @error set
672 : : *
673 : : * Since: 1.0
674 : : */
675 : : gboolean
676 : 1 : valent_channel_close_finish (ValentChannel *channel,
677 : : GAsyncResult *result,
678 : : GError **error)
679 : : {
680 : 1 : gboolean ret;
681 : :
682 : 1 : VALENT_ENTRY;
683 : :
684 [ + - ]: 1 : g_return_val_if_fail (VALENT_IS_CHANNEL (channel), FALSE);
685 [ - + ]: 1 : g_return_val_if_fail (g_task_is_valid (result, channel), FALSE);
686 [ + - - + ]: 1 : g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
687 : :
688 : 1 : ret = g_task_propagate_boolean (G_TASK (result), error);
689 : :
690 : 1 : VALENT_RETURN (ret);
691 : : }
692 : :
693 : : static void
694 : 357 : valent_channel_read_packet_task (GTask *task,
695 : : gpointer source_object,
696 : : gpointer task_data,
697 : : GCancellable *cancellable)
698 : : {
699 : 357 : ValentChannel *self = VALENT_CHANNEL (source_object);
700 : 357 : ValentChannelPrivate *priv = valent_channel_get_instance_private (self);
701 : 357 : g_autoptr (GDataInputStream) stream = NULL;
702 [ + - + + ]: 357 : g_autofree char *line = NULL;
703 : 357 : JsonNode *packet = NULL;
704 : 357 : GError *error = NULL;
705 : :
706 [ + + ]: 357 : if (valent_channel_return_error_if_closed (self, task))
707 : : return;
708 : :
709 : 351 : stream = g_object_ref (priv->input_buffer);
710 : 351 : valent_object_unlock (VALENT_OBJECT (self));
711 : :
712 : 351 : line = g_data_input_stream_read_line_utf8 (stream, NULL, cancellable, &error);
713 : :
714 [ + + ]: 351 : if (error != NULL)
715 : 8 : return g_task_return_error (task, error);
716 : :
717 [ + + ]: 343 : if (line == NULL)
718 : 64 : return g_task_return_new_error (task,
719 : : G_IO_ERROR,
720 : : G_IO_ERROR_CONNECTION_CLOSED,
721 : : "Channel is closed");
722 : :
723 [ - + ]: 279 : if ((packet = valent_packet_deserialize (line, &error)) == NULL)
724 : 0 : return g_task_return_error (task, error);
725 : :
726 : 279 : g_task_return_pointer (task, packet, (GDestroyNotify)json_node_unref);
727 : : }
728 : :
729 : : /**
730 : : * valent_channel_read_packet:
731 : : * @channel: a `ValentChannel`
732 : : * @cancellable: (nullable): a `GCancellable`
733 : : * @callback: (scope async): a `GAsyncReadyCallback`
734 : : * @user_data: user supplied data
735 : : *
736 : : * Read the next KDE Connect packet from @channel.
737 : : *
738 : : * Call [method@Valent.Channel.read_packet_finish] to get the result.
739 : : *
740 : : * Since: 1.0
741 : : */
742 : : void
743 : 357 : valent_channel_read_packet (ValentChannel *channel,
744 : : GCancellable *cancellable,
745 : : GAsyncReadyCallback callback,
746 : : gpointer user_data)
747 : : {
748 : 714 : g_autoptr (GTask) task = NULL;
749 : :
750 : 357 : VALENT_ENTRY;
751 : :
752 [ + - ]: 357 : g_return_if_fail (VALENT_IS_CHANNEL (channel));
753 [ - + - - : 357 : g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- - - - ]
754 : :
755 : 357 : task = g_task_new (channel, cancellable, callback, user_data);
756 [ + - ]: 357 : g_task_set_source_tag (task, valent_channel_read_packet);
757 : 357 : g_task_run_in_thread (task, valent_channel_read_packet_task);
758 : :
759 [ + - ]: 357 : VALENT_EXIT;
760 : : }
761 : :
762 : : /**
763 : : * valent_channel_read_packet_finish:
764 : : * @channel: a `ValentChannel`
765 : : * @result: a `GAsyncResult`
766 : : * @error: (nullable): a `GError`
767 : : *
768 : : * Finish an operation started by [method@Valent.Channel.read_packet].
769 : : *
770 : : * Returns: (transfer full): a KDE Connect packet, or %NULL with @error set
771 : : *
772 : : * Since: 1.0
773 : : */
774 : : JsonNode *
775 : 356 : valent_channel_read_packet_finish (ValentChannel *channel,
776 : : GAsyncResult *result,
777 : : GError **error)
778 : : {
779 : 356 : JsonNode *ret;
780 : :
781 : 356 : VALENT_ENTRY;
782 : :
783 [ + - ]: 356 : g_return_val_if_fail (VALENT_IS_CHANNEL (channel), NULL);
784 [ - + ]: 356 : g_return_val_if_fail (g_task_is_valid (result, channel), NULL);
785 [ + - - + ]: 356 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
786 : :
787 : 356 : ret = g_task_propagate_pointer (G_TASK (result), error);
788 : :
789 : 356 : VALENT_RETURN (ret);
790 : : }
791 : :
792 : : static gboolean
793 : 291 : valent_channel_write_packet_func (gpointer data)
794 : : {
795 : 291 : GTask *task = G_TASK (data);
796 : 291 : ValentChannel *self = g_task_get_source_object (task);
797 : 291 : ValentChannelPrivate *priv = valent_channel_get_instance_private (self);
798 : 582 : g_autoptr (GOutputStream) stream = NULL;
799 : 291 : JsonNode *packet = NULL;
800 : 291 : GCancellable *cancellable = NULL;
801 : 291 : GError *error = NULL;
802 : :
803 [ + - + - : 291 : g_assert (G_IS_TASK (task));
- + - - ]
804 [ - + ]: 291 : g_assert (VALENT_IS_CHANNEL (self));
805 : :
806 [ + - ]: 291 : if (valent_channel_return_error_if_closed (self, task))
807 : : return G_SOURCE_REMOVE;
808 : :
809 : 291 : stream = g_object_ref (g_io_stream_get_output_stream (priv->base_stream));
810 : 291 : valent_object_unlock (VALENT_OBJECT (self));
811 : :
812 : 291 : packet = g_task_get_task_data (task);
813 : 291 : cancellable = g_task_get_cancellable (task);
814 : :
815 [ + + ]: 291 : if (valent_packet_to_stream (stream, packet, cancellable, &error))
816 : 286 : g_task_return_boolean (task, TRUE);
817 : : else
818 : 5 : g_task_return_error (task, error);
819 : :
820 [ + - ]: 291 : return G_SOURCE_REMOVE;
821 : : }
822 : :
823 : : /**
824 : : * valent_channel_write_packet:
825 : : * @channel: a `ValentChannel`
826 : : * @packet: a KDE Connect packet
827 : : * @cancellable: (nullable): a `GCancellable`
828 : : * @callback: (scope async): a `GAsyncReadyCallback`
829 : : * @user_data: user supplied data
830 : : *
831 : : * Send a packet over the channel.
832 : : *
833 : : * Internally [class@Valent.Channel] uses an outgoing packet buffer, so
834 : : * multiple requests can be started safely from any thread.
835 : : *
836 : : * Call [method@Valent.Channel.write_packet_finish] to get the result.
837 : : *
838 : : * Since: 1.0
839 : : */
840 : : void
841 : 292 : valent_channel_write_packet (ValentChannel *channel,
842 : : JsonNode *packet,
843 : : GCancellable *cancellable,
844 : : GAsyncReadyCallback callback,
845 : : gpointer user_data)
846 : : {
847 : 292 : ValentChannelPrivate *priv = valent_channel_get_instance_private (channel);
848 : 584 : g_autoptr (GTask) task = NULL;
849 : :
850 : 292 : VALENT_ENTRY;
851 : :
852 [ + - ]: 292 : g_return_if_fail (VALENT_IS_CHANNEL (channel));
853 [ - + ]: 292 : g_return_if_fail (VALENT_IS_PACKET (packet));
854 [ + + + - : 292 : g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- + - - ]
855 : :
856 : 292 : task = g_task_new (channel, cancellable, callback, user_data);
857 [ + - ]: 292 : g_task_set_source_tag (task, valent_channel_write_packet);
858 : 292 : g_task_set_task_data (task,
859 : 292 : json_node_ref (packet),
860 : : (GDestroyNotify)json_node_unref);
861 : :
862 [ + + ]: 292 : if (valent_channel_return_error_if_closed (channel, task))
863 : 291 : VALENT_EXIT;
864 : :
865 : 291 : g_main_context_invoke_full (g_main_loop_get_context (priv->output_buffer),
866 : : g_task_get_priority (task),
867 : : valent_channel_write_packet_func,
868 : : g_object_ref (task),
869 : : g_object_unref);
870 : :
871 : 291 : valent_object_unlock (VALENT_OBJECT (channel));
872 : :
873 [ + - ]: 583 : VALENT_EXIT;
874 : : }
875 : :
876 : : /**
877 : : * valent_channel_write_packet_finish:
878 : : * @channel: a `ValentChannel`
879 : : * @result: a `GAsyncResult`
880 : : * @error: (nullable): a `GError`
881 : : *
882 : : * Finish an operation started by [method@Valent.Channel.write_packet].
883 : : *
884 : : * Returns: %TRUE if successful, or %FALSE with @error set
885 : : *
886 : : * Since: 1.0
887 : : */
888 : : gboolean
889 : 257 : valent_channel_write_packet_finish (ValentChannel *channel,
890 : : GAsyncResult *result,
891 : : GError **error)
892 : : {
893 : 257 : gboolean ret;
894 : :
895 : 257 : VALENT_ENTRY;
896 : :
897 [ + - ]: 257 : g_return_val_if_fail (VALENT_IS_CHANNEL (channel), FALSE);
898 [ - + ]: 257 : g_return_val_if_fail (g_task_is_valid (result, channel), FALSE);
899 [ + - - + ]: 257 : g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
900 : :
901 : 257 : ret = g_task_propagate_boolean (G_TASK (result), error);
902 : :
903 : 257 : VALENT_RETURN (ret);
904 : : }
905 : :
906 : : /**
907 : : * valent_channel_store_data: (virtual store_data)
908 : : * @channel: a `ValentChannel`
909 : : * @context: a `ValentContext`
910 : : *
911 : : * Store channel metadata.
912 : : *
913 : : * This method is called to store channel specific data. Implementations can
914 : : * override this method to store extra data (eg. TLS Certificate).
915 : : *
916 : : * Implementations that override [vfunc@Valent.Channel.store_data] must
917 : : * chain-up.
918 : : *
919 : : * Since: 1.0
920 : : */
921 : : void
922 : 5 : valent_channel_store_data (ValentChannel *channel,
923 : : ValentContext *context)
924 : : {
925 : 5 : VALENT_ENTRY;
926 : :
927 [ + - ]: 5 : g_return_if_fail (VALENT_IS_CHANNEL (channel));
928 [ - + ]: 5 : g_return_if_fail (VALENT_IS_CONTEXT (context));
929 : :
930 : 5 : VALENT_CHANNEL_GET_CLASS (channel)->store_data (channel, context);
931 : :
932 : 5 : VALENT_EXIT;
933 : : }
934 : :
935 : : /**
936 : : * valent_channel_download: (virtual download)
937 : : * @channel: a `ValentChannel`
938 : : * @packet: a KDE Connect packet
939 : : * @cancellable: (nullable): a `GCancellable`
940 : : * @error: (nullable): a `GError`
941 : : *
942 : : * Open an auxiliary connection, usually to download data.
943 : : *
944 : : * Implementations should use information from the `payloadTransferInfo` field
945 : : * to open a connection and wait for it to be accepted. In most cases the remote
946 : : * device will write data to the stream and then close it when finished.
947 : : *
948 : : * For example, a TCP-based implementation could connect to a port in the
949 : : * `payloadTransferInfo` dictionary on the same host as the channel. When the
950 : : * connection is accepted the caller can perform operations on it as required.
951 : : *
952 : : * Returns: (transfer full) (nullable): a `GIOStream`
953 : : *
954 : : * Since: 1.0
955 : : */
956 : : GIOStream *
957 : 27 : valent_channel_download (ValentChannel *channel,
958 : : JsonNode *packet,
959 : : GCancellable *cancellable,
960 : : GError **error)
961 : : {
962 : 27 : GIOStream *ret;
963 : :
964 : 27 : VALENT_ENTRY;
965 : :
966 [ + - ]: 27 : g_return_val_if_fail (VALENT_IS_CHANNEL (channel), NULL);
967 [ - + ]: 27 : g_return_val_if_fail (VALENT_IS_PACKET (packet), NULL);
968 [ + + + - : 27 : g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
- + - - ]
969 [ + - - + ]: 27 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
970 : :
971 : 27 : ret = VALENT_CHANNEL_GET_CLASS (channel)->download (channel,
972 : : packet,
973 : : cancellable,
974 : : error);
975 : :
976 : 27 : VALENT_RETURN (ret);
977 : : }
978 : :
979 : : /**
980 : : * valent_channel_download_async: (virtual download_async)
981 : : * @channel: a `ValentChannel`
982 : : * @packet: a KDE Connect packet
983 : : * @cancellable: (nullable): a `GCancellable`
984 : : * @callback: (scope async): a `GAsyncReadyCallback`
985 : : * @user_data: user supplied data
986 : : *
987 : : * Open an auxiliary connection, usually to download data.
988 : : *
989 : : * This is a non-blocking variant of [method@Valent.Channel.download]. Call
990 : : * [method@Valent.Channel.download_finish] to get the result.
991 : : *
992 : : * The default implementation of this method invokes
993 : : * [vfunc@Valent.Channel.download] in a thread.
994 : : *
995 : : * Since: 1.0
996 : : */
997 : : void
998 : 1 : valent_channel_download_async (ValentChannel *channel,
999 : : JsonNode *packet,
1000 : : GCancellable *cancellable,
1001 : : GAsyncReadyCallback callback,
1002 : : gpointer user_data)
1003 : : {
1004 : 1 : VALENT_ENTRY;
1005 : :
1006 [ + - ]: 1 : g_return_if_fail (VALENT_IS_CHANNEL (channel));
1007 [ - + ]: 1 : g_return_if_fail (VALENT_IS_PACKET (packet));
1008 [ - + - - : 1 : g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- - - - ]
1009 : :
1010 : 1 : VALENT_CHANNEL_GET_CLASS (channel)->download_async (channel,
1011 : : packet,
1012 : : cancellable,
1013 : : callback,
1014 : : user_data);
1015 : :
1016 : 1 : VALENT_EXIT;
1017 : : }
1018 : :
1019 : : /**
1020 : : * valent_channel_download_finish: (virtual download_finish)
1021 : : * @channel: a `ValentChannel`
1022 : : * @result: a `GAsyncResult`
1023 : : * @error: (nullable): a `GError`
1024 : : *
1025 : : * Finish an operation started with [method@Valent.Channel.download_async].
1026 : : *
1027 : : * Returns: (transfer full) (nullable): a `GIOStream`
1028 : : *
1029 : : * Since: 1.0
1030 : : */
1031 : : GIOStream *
1032 : 1 : valent_channel_download_finish (ValentChannel *channel,
1033 : : GAsyncResult *result,
1034 : : GError **error)
1035 : : {
1036 : 1 : GIOStream *ret;
1037 : :
1038 : 1 : VALENT_ENTRY;
1039 : :
1040 [ + - ]: 1 : g_return_val_if_fail (VALENT_IS_CHANNEL (channel), NULL);
1041 [ - + ]: 1 : g_return_val_if_fail (g_task_is_valid (result, channel), NULL);
1042 [ + - - + ]: 1 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1043 : :
1044 : 1 : ret = VALENT_CHANNEL_GET_CLASS (channel)->download_finish (channel,
1045 : : result,
1046 : : error);
1047 : :
1048 : 1 : VALENT_RETURN (ret);
1049 : : }
1050 : :
1051 : : /**
1052 : : * valent_channel_upload: (virtual upload)
1053 : : * @channel: a `ValentChannel`
1054 : : * @packet: a KDE Connect packet
1055 : : * @cancellable: (nullable): a `GCancellable`
1056 : : * @error: (nullable): a `GError`
1057 : : *
1058 : : * Accept an auxiliary connection, usually to upload data.
1059 : : *
1060 : : * Implementations should set the `payloadTransferInfo` field with information
1061 : : * the peer can use to open a connection and wait to accept that connection. In
1062 : : * most cases the remote device with expect the caller to write to the stream
1063 : : * and then close it when finished.
1064 : : *
1065 : : * For example, a TCP-based implementation could start listening on a port then
1066 : : * send the packet with that port in the `payloadTransferInfo` dictionary. When
1067 : : * a connection is accepted the caller can perform operations on it as required.
1068 : : *
1069 : : * Returns: (transfer full) (nullable): a `GIOStream`
1070 : : *
1071 : : * Since: 1.0
1072 : : */
1073 : : GIOStream *
1074 : 27 : valent_channel_upload (ValentChannel *channel,
1075 : : JsonNode *packet,
1076 : : GCancellable *cancellable,
1077 : : GError **error)
1078 : : {
1079 : 27 : GIOStream *ret;
1080 : :
1081 : 27 : VALENT_ENTRY;
1082 : :
1083 [ + - ]: 27 : g_return_val_if_fail (VALENT_IS_CHANNEL (channel), NULL);
1084 [ - + ]: 27 : g_return_val_if_fail (VALENT_IS_PACKET (packet), NULL);
1085 [ + + + - : 27 : g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
- + - - ]
1086 [ + - - + ]: 27 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1087 : :
1088 : 27 : ret = VALENT_CHANNEL_GET_CLASS (channel)->upload (channel,
1089 : : packet,
1090 : : cancellable,
1091 : : error);
1092 : :
1093 : 27 : VALENT_RETURN (ret);
1094 : : }
1095 : :
1096 : : /**
1097 : : * valent_channel_upload_async: (virtual upload_async)
1098 : : * @channel: a `ValentChannel`
1099 : : * @packet: a KDE Connect packet
1100 : : * @cancellable: (nullable): a `GCancellable`
1101 : : * @callback: (scope async): a `GAsyncReadyCallback`
1102 : : * @user_data: user supplied data
1103 : : *
1104 : : * Accept an auxiliary connection, usually to upload data.
1105 : : *
1106 : : * This is a non-blocking variant of [method@Valent.Channel.upload]. Call
1107 : : * [method@Valent.Channel.upload_finish] to get the result.
1108 : : *
1109 : : * The default implementation of this method invokes
1110 : : * [vfunc@Valent.Channel.upload] in a thread.
1111 : : *
1112 : : * Since: 1.0
1113 : : */
1114 : : void
1115 : 1 : valent_channel_upload_async (ValentChannel *channel,
1116 : : JsonNode *packet,
1117 : : GCancellable *cancellable,
1118 : : GAsyncReadyCallback callback,
1119 : : gpointer user_data)
1120 : : {
1121 : 1 : VALENT_ENTRY;
1122 : :
1123 [ + - ]: 1 : g_return_if_fail (VALENT_IS_CHANNEL (channel));
1124 [ - + ]: 1 : g_return_if_fail (VALENT_IS_PACKET (packet));
1125 [ - + - - : 1 : g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- - - - ]
1126 : :
1127 : 1 : VALENT_CHANNEL_GET_CLASS (channel)->upload_async (channel,
1128 : : packet,
1129 : : cancellable,
1130 : : callback,
1131 : : user_data);
1132 : :
1133 : 1 : VALENT_EXIT;
1134 : : }
1135 : :
1136 : : /**
1137 : : * valent_channel_upload_finish: (virtual upload_finish)
1138 : : * @channel: a `ValentChannel`
1139 : : * @result: a `GAsyncResult`
1140 : : * @error: (nullable): a `GError`
1141 : : *
1142 : : * Finish an operation started with [method@Valent.Channel.upload_async].
1143 : : *
1144 : : * Returns: (transfer full) (nullable): a `GIOStream`
1145 : : *
1146 : : * Since: 1.0
1147 : : */
1148 : : GIOStream *
1149 : 1 : valent_channel_upload_finish (ValentChannel *channel,
1150 : : GAsyncResult *result,
1151 : : GError **error)
1152 : : {
1153 : 1 : GIOStream *ret;
1154 : :
1155 : 1 : VALENT_ENTRY;
1156 : :
1157 [ + - ]: 1 : g_return_val_if_fail (VALENT_IS_CHANNEL (channel), NULL);
1158 [ - + ]: 1 : g_return_val_if_fail (g_task_is_valid (result, channel), NULL);
1159 [ + - - + ]: 1 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1160 : :
1161 : 1 : ret = VALENT_CHANNEL_GET_CLASS (channel)->upload_finish (channel,
1162 : : result,
1163 : : error);
1164 : :
1165 : 1 : VALENT_RETURN (ret);
1166 : : }
1167 : :
|