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