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