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-core"
5 : :
6 : : #include "config.h"
7 : :
8 : : #include <gio/gio.h>
9 : : #include <json-glib/json-glib.h>
10 : :
11 : : #include "../core/valent-global.h"
12 : : #include "valent-packet.h"
13 : :
14 : :
15 [ + + ]: 13 : G_DEFINE_QUARK (valent-packet-error, valent_packet_error)
16 : :
17 : :
18 : : /**
19 : : * valent_packet_new:
20 : : * @type: a KDE Connect packet type
21 : : *
22 : : * A convenience function for creating a new KDE Connect packet with the type
23 : : * field set to @type.
24 : : *
25 : : * Returns: (transfer full): a KDE Connect packet
26 : : *
27 : : * Since: 1.0
28 : : */
29 : : JsonNode *
30 : 27 : valent_packet_new (const char *type)
31 : : {
32 : 54 : g_autoptr (JsonBuilder) builder = NULL;
33 : :
34 [ - + ]: 27 : g_return_val_if_fail (type != NULL, NULL);
35 : :
36 : 27 : builder = json_builder_new ();
37 : :
38 : 27 : json_builder_begin_object (builder);
39 : 27 : json_builder_set_member_name (builder, "id");
40 : 27 : json_builder_add_int_value (builder, 0);
41 : 27 : json_builder_set_member_name (builder, "type");
42 : 27 : json_builder_add_string_value (builder, type);
43 : 27 : json_builder_set_member_name (builder, "body");
44 : 27 : json_builder_end_object (json_builder_begin_object (builder));
45 : 27 : json_builder_end_object (builder);
46 : :
47 : 27 : return json_builder_get_root (builder);
48 : : }
49 : :
50 : :
51 : : /**
52 : : * valent_packet_init: (skip)
53 : : * @builder: a location to initialize a `JsonBuilder`
54 : : * @type: a KDE Connect packet type
55 : : *
56 : : * Initialize a [class@Json.Builder] and KDE Connect packet.
57 : : *
58 : : * Creates a new [class@Json.Builder] and initializes a packet for @type,
59 : : * leaving the builder in the `body` object. Call [func@Valent.packet_end]
60 : : * to finish the packet and get the result.
61 : : *
62 : : * ```c
63 : : * g_autoptr (JsonBuilder) builder = NULL;
64 : : * g_autoptr (JsonNode) packet = NULL;
65 : : *
66 : : * valent_packet_init (&builder, "kdeconnect.ping");
67 : : * json_builder_set_member_name (builder, "message");
68 : : * json_builder_add_string_value (builder, "Ping!");
69 : : * packet = valent_packet_end (&builder);
70 : : * ```
71 : : *
72 : : *
73 : : * Since: 1.0
74 : : */
75 : : void
76 : 171 : valent_packet_init (JsonBuilder **builder,
77 : : const char *type)
78 : : {
79 [ + - + - ]: 171 : g_return_if_fail (builder != NULL && *builder == NULL);
80 [ + - + - ]: 171 : g_return_if_fail (type != NULL && *type != '\0');
81 : :
82 : 171 : *builder = json_builder_new ();
83 : 171 : json_builder_begin_object (*builder);
84 : 171 : json_builder_set_member_name (*builder, "id");
85 : 171 : json_builder_add_int_value (*builder, 0);
86 : 171 : json_builder_set_member_name (*builder, "type");
87 : 171 : json_builder_add_string_value (*builder, type);
88 : 171 : json_builder_set_member_name (*builder, "body");
89 : :
90 : 171 : json_builder_begin_object (*builder);
91 : : }
92 : :
93 : : /**
94 : : * valent_packet_end: (skip)
95 : : * @builder: a pointer to a `JsonBuilder`
96 : : *
97 : : * Finish a packet created with [func@Valent.packet_init].
98 : : *
99 : : * This function closes the `body` and root objects, then calls
100 : : * [method@Json.Builder.get_root]. Then the reference count of @builder is
101 : : * decreased and the pointer is set to %NULL, before returning the packet.
102 : : *
103 : : * Returns: (transfer full) (nullable): a KDE Connect packet
104 : : *
105 : : * Since: 1.0
106 : : */
107 : : JsonNode *
108 : 171 : valent_packet_end (JsonBuilder **builder)
109 : : {
110 : 171 : JsonNode *ret = NULL;
111 : :
112 [ + - + - : 171 : g_return_val_if_fail (builder != NULL && JSON_IS_BUILDER (*builder), NULL);
+ - - + -
- ]
113 : :
114 : : /* Finish the `body` object and the root object */
115 : 171 : json_builder_end_object (*builder);
116 : 171 : json_builder_end_object (*builder);
117 : :
118 : 171 : ret = json_builder_get_root (*builder);
119 [ + - ]: 171 : g_clear_object (builder);
120 : :
121 : : return g_steal_pointer (&ret);
122 : : }
123 : :
124 : : /**
125 : : * valent_packet_get_id:
126 : : * @packet: a KDE Connect packet
127 : : *
128 : : * Convenience function for getting the timestamp of a KDE Connect packet.
129 : : *
130 : : * Returns: a UNIX epoch timestamp
131 : : *
132 : : * Since: 1.0
133 : : */
134 : : int64_t
135 : 1 : valent_packet_get_id (JsonNode *packet)
136 : : {
137 : 1 : JsonObject *root;
138 : 1 : JsonNode *node;
139 : :
140 [ - + ]: 1 : g_return_val_if_fail (JSON_NODE_HOLDS_OBJECT (packet), 0);
141 : :
142 : 1 : root = json_node_get_object (packet);
143 : :
144 [ + - - + ]: 1 : if G_UNLIKELY ((node = json_object_get_member (root, "id")) == NULL ||
145 : : json_node_get_value_type (node) != G_TYPE_INT64)
146 : 0 : g_return_val_if_reached (0);
147 : :
148 : 1 : return json_node_get_int (node);
149 : : }
150 : :
151 : : /**
152 : : * valent_packet_get_type:
153 : : * @packet: a KDE Connect packet
154 : : *
155 : : * Convenience function for getting the capability type of a KDE Connect packet.
156 : : *
157 : : * Returns: (transfer none) (nullable): a KDE Connect capability
158 : : *
159 : : * Since: 1.0
160 : : */
161 : : const char *
162 : 295 : valent_packet_get_type (JsonNode *packet)
163 : : {
164 : 295 : JsonObject *root;
165 : 295 : JsonNode *node;
166 : :
167 [ - + ]: 295 : g_return_val_if_fail (JSON_NODE_HOLDS_OBJECT (packet), NULL);
168 : :
169 : 295 : root = json_node_get_object (packet);
170 : :
171 [ + - - + ]: 295 : if G_UNLIKELY ((node = json_object_get_member (root, "type")) == NULL ||
172 : : json_node_get_value_type (node) != G_TYPE_STRING)
173 : 0 : g_return_val_if_reached (NULL);
174 : :
175 : 295 : return json_node_get_string (node);
176 : : }
177 : :
178 : : /**
179 : : * valent_packet_get_body:
180 : : * @packet: a KDE Connect packet
181 : : *
182 : : * Convenience function for getting the packet body of a KDE Connect packet.
183 : : *
184 : : * Returns: (transfer none) (nullable): a `JsonObject`
185 : : *
186 : : * Since: 1.0
187 : : */
188 : : JsonObject *
189 : 618 : valent_packet_get_body (JsonNode *packet)
190 : : {
191 : 618 : JsonObject *root;
192 : 618 : JsonNode *node;
193 : :
194 [ - + ]: 618 : g_return_val_if_fail (JSON_NODE_HOLDS_OBJECT (packet), NULL);
195 : :
196 : 618 : root = json_node_get_object (packet);
197 : :
198 [ + - - + ]: 618 : if G_UNLIKELY ((node = json_object_get_member (root, "body")) == NULL ||
199 : : json_node_get_node_type (node) != JSON_NODE_OBJECT)
200 : 0 : g_return_val_if_reached (NULL);
201 : :
202 : 618 : return json_node_get_object (node);
203 : : }
204 : :
205 : : /**
206 : : * valent_packet_has_payload:
207 : : * @packet: a KDE Connect packet
208 : : *
209 : : * Return %TRUE if the packet holds valid transfer information. Payload
210 : : * information is considered invalid in the following cases:
211 : : *
212 : : * - The `payloadSize` field is present, but not a %G_TYPE_INT64
213 : : * - The `payloadTransferInfo` field is missing from the root object
214 : : * - The `payloadTransferInfo` field is not a %JSON_NODE_OBJECT
215 : : *
216 : : * Returns: %TRUE if @packet has a payload
217 : : *
218 : : * Since: 1.0
219 : : */
220 : : gboolean
221 : 52 : valent_packet_has_payload (JsonNode *packet)
222 : : {
223 : 52 : JsonObject *root;
224 : 52 : JsonNode *node;
225 : :
226 [ - + ]: 52 : g_return_val_if_fail (VALENT_IS_PACKET (packet), FALSE);
227 : :
228 : 52 : root = json_node_get_object (packet);
229 : :
230 [ + + - + ]: 96 : if ((node = json_object_get_member (root, "payloadSize")) != NULL &&
231 : 44 : json_node_get_value_type (node) != G_TYPE_INT64)
232 : : return FALSE;
233 : :
234 [ + + - + ]: 86 : if ((node = json_object_get_member (root, "payloadTransferInfo")) == NULL ||
235 : 34 : json_node_get_node_type (node) != JSON_NODE_OBJECT)
236 : 18 : return FALSE;
237 : :
238 : : return TRUE;
239 : : }
240 : :
241 : : /**
242 : : * valent_packet_get_payload_full:
243 : : * @packet: a KDE Connect packet
244 : : * @size: (out) (nullable): the payload size
245 : : * @error: (nullable): a `GError`
246 : : *
247 : : * A convenience for retrieving the `payloadTransferInfo` and `payloadSize`
248 : : * fields from @packet.
249 : : *
250 : : * If @packet is malformed or missing payload information, %NULL will be
251 : : * returned with @error set. See valent_packet_has_payload() for validation
252 : : * criteria.
253 : : *
254 : : * Returns: (transfer none) (nullable): a `JsonObject`
255 : : *
256 : : * Since: 1.0
257 : : */
258 : : JsonObject *
259 : 31 : valent_packet_get_payload_full (JsonNode *packet,
260 : : goffset *size,
261 : : GError **error)
262 : : {
263 : 31 : JsonObject *root;
264 : 31 : JsonNode *node;
265 : :
266 [ - + ]: 31 : if (!valent_packet_validate (packet, error))
267 : : return NULL;
268 : :
269 : 31 : root = json_node_get_object (packet);
270 : :
271 : : /* The documentation implies that this field could be missing or have a value
272 : : * of `-1` to indicate the length is indefinite (eg. for streaming). */
273 [ + - - + ]: 62 : if ((node = json_object_get_member (root, "payloadSize")) != NULL &&
274 : 31 : json_node_get_value_type (node) != G_TYPE_INT64)
275 : : {
276 : 0 : g_set_error_literal (error,
277 : : VALENT_PACKET_ERROR,
278 : : VALENT_PACKET_ERROR_INVALID_FIELD,
279 : : "expected \"payloadSize\" field to hold an integer");
280 : 0 : return NULL;
281 : : }
282 : :
283 [ + - ]: 31 : if (size != NULL)
284 [ + - ]: 31 : *size = node ? json_node_get_int (node) : -1;
285 : :
286 [ + - - + ]: 62 : if ((node = json_object_get_member (root, "payloadTransferInfo")) == NULL ||
287 : 31 : json_node_get_node_type (node) != JSON_NODE_OBJECT)
288 : : {
289 : 0 : g_set_error_literal (error,
290 : : VALENT_PACKET_ERROR,
291 : : node == NULL
292 : : ? VALENT_PACKET_ERROR_MISSING_FIELD
293 : : : VALENT_PACKET_ERROR_INVALID_FIELD,
294 : : "expected \"payloadTransferInfo\" field holding an object");
295 : 0 : return NULL;
296 : : }
297 : :
298 : 31 : return json_node_get_object (node);
299 : : }
300 : :
301 : : /**
302 : : * valent_packet_set_payload_full:
303 : : * @packet: a KDE Connect packet
304 : : * @info: (transfer full): a `JsonObject`
305 : : * @size: the payload size in bytes
306 : : *
307 : : * A convenience method for setting the `payloadTransferInfo` and `payloadSize`
308 : : * fields on @packet.
309 : : *
310 : : * Since: 1.0
311 : : */
312 : : void
313 : 1 : valent_packet_set_payload_full (JsonNode *packet,
314 : : JsonObject *info,
315 : : goffset size)
316 : : {
317 : 1 : JsonObject *root;
318 : :
319 [ - + ]: 1 : g_return_if_fail (VALENT_IS_PACKET (packet));
320 : :
321 : 1 : root = json_node_get_object (packet);
322 : :
323 : 1 : json_object_set_object_member (root, "payloadTransferInfo", info);
324 : 1 : json_object_set_int_member (root, "payloadSize", (int64_t)size);
325 : : }
326 : :
327 : : /**
328 : : * valent_packet_get_payload_info:
329 : : * @packet: a KDE Connect packet
330 : : *
331 : : * A convenience for retrieve the 'payloadTransferInfo` field from @packet.
332 : : *
333 : : * Returns: (transfer none) (nullable): a `JsonObject`
334 : : *
335 : : * Since: 1.0
336 : : */
337 : : JsonObject *
338 : 1 : valent_packet_get_payload_info (JsonNode *packet)
339 : : {
340 : 1 : JsonNode *node;
341 : :
342 [ - + ]: 1 : g_return_val_if_fail (VALENT_IS_PACKET (packet), NULL);
343 : :
344 : 1 : node = json_object_get_member (json_node_get_object (packet),
345 : : "payloadTransferInfo");
346 : :
347 [ + - - + ]: 1 : if G_UNLIKELY (node == NULL || !JSON_NODE_HOLDS_OBJECT (node))
348 : 0 : g_return_val_if_reached (NULL);
349 : :
350 : 1 : return json_node_get_object (node);
351 : : }
352 : :
353 : : /**
354 : : * valent_packet_set_payload_info:
355 : : * @packet: a KDE Connect packet
356 : : * @info: (transfer full): a `JsonObject`
357 : : *
358 : : * A convenience method for setting the `payloadTransferInfo` field on @packet.
359 : : *
360 : : * Since: 1.0
361 : : */
362 : : void
363 : 31 : valent_packet_set_payload_info (JsonNode *packet,
364 : : JsonObject *info)
365 : : {
366 : 31 : JsonObject *root;
367 : :
368 [ - + ]: 31 : g_return_if_fail (VALENT_IS_PACKET (packet));
369 [ + - ]: 31 : g_return_if_fail (info != NULL);
370 : :
371 : 31 : root = json_node_get_object (packet);
372 : :
373 : 31 : json_object_set_object_member (root, "payloadTransferInfo", info);
374 : : }
375 : :
376 : : /**
377 : : * valent_packet_get_payload_size:
378 : : * @packet: a KDE Connect packet
379 : : *
380 : : * Get the `payloadSize` field of @packet in bytes.
381 : : *
382 : : * Returns: the payload size
383 : : *
384 : : * Since: 1.0
385 : : */
386 : : goffset
387 : 65 : valent_packet_get_payload_size (JsonNode *packet)
388 : : {
389 : 65 : JsonObject *root;
390 : 65 : JsonNode *node;
391 : :
392 [ - + ]: 65 : g_return_val_if_fail (VALENT_IS_PACKET (packet), 0);
393 : :
394 : 65 : root = json_node_get_object (packet);
395 : 65 : node = json_object_get_member (root, "payloadSize");
396 : :
397 [ + - - + ]: 130 : if ((node = json_object_get_member (root, "payloadSize")) != NULL &&
398 : 65 : json_node_get_value_type (node) != G_TYPE_INT64)
399 : 0 : g_return_val_if_reached (-1);
400 : :
401 : 65 : return node ? json_node_get_int (node) : -1;
402 : : }
403 : :
404 : : /**
405 : : * valent_packet_set_payload_size:
406 : : * @packet: a KDE Connect packet
407 : : * @size: the payload size in bytes
408 : : *
409 : : * Set the `payloadSize` field of @packet to @size.
410 : : *
411 : : * Since: 1.0
412 : : */
413 : : void
414 : 41 : valent_packet_set_payload_size (JsonNode *packet,
415 : : goffset size)
416 : : {
417 : 41 : JsonObject *root;
418 : :
419 [ - + ]: 41 : g_return_if_fail (VALENT_IS_PACKET (packet));
420 [ + - ]: 41 : g_return_if_fail (size >= -1);
421 : :
422 : 41 : root = json_node_get_object (packet);
423 : :
424 : 41 : json_object_set_int_member (root, "payloadSize", (int64_t)size);
425 : : }
426 : :
427 : : /**
428 : : * valent_packet_check_field:
429 : : * @packet: a KDE Connect packet
430 : : * @field: (not nullable): field name
431 : : *
432 : : * Check @packet for @field and return %TRUE if present, with two exceptions:
433 : : *
434 : : * 1. If @field is a %G_TYPE_BOOLEAN, its value is returned
435 : : * 2. If @field is a %G_TYPE_STRING, %FALSE is returned if the string is empty.
436 : : *
437 : : * Returns: %TRUE, or %FALSE on failure
438 : : *
439 : : * Since: 1.0
440 : : */
441 : : gboolean
442 : 126 : valent_packet_check_field (JsonNode *packet,
443 : : const char *field)
444 : : {
445 : 126 : JsonObject *root;
446 : 126 : JsonObject *body;
447 : 126 : JsonNode *node;
448 : :
449 [ - + ]: 126 : g_return_val_if_fail (JSON_NODE_HOLDS_OBJECT (packet), FALSE);
450 [ + - + - ]: 126 : g_return_val_if_fail (field != NULL && *field != '\0', FALSE);
451 : :
452 : 126 : root = json_node_get_object (packet);
453 : :
454 [ + - - + ]: 126 : if G_UNLIKELY ((node = json_object_get_member (root, "body")) == NULL ||
455 : : json_node_get_node_type (node) != JSON_NODE_OBJECT)
456 : 0 : return FALSE;
457 : :
458 : 126 : body = json_node_get_object (node);
459 : :
460 [ + + ]: 126 : if G_UNLIKELY ((node = json_object_get_member (body, field)) == NULL)
461 : : return FALSE;
462 : :
463 [ + + ]: 49 : if (json_node_get_value_type (node) == G_TYPE_BOOLEAN)
464 : 34 : return json_node_get_boolean (node);
465 : :
466 [ + + ]: 15 : if (json_node_get_value_type (node) == G_TYPE_STRING)
467 : 13 : return json_node_get_string (node)[0] != '\0';
468 : :
469 : : return TRUE;
470 : : }
471 : :
472 : : /**
473 : : * valent_packet_get_boolean:
474 : : * @packet: a KDE Connect packet
475 : : * @field: (not nullable): field name
476 : : * @value: (out) (nullable): a boolean
477 : : *
478 : : * Lookup @field in the body of @packet and assign it to @value.
479 : : *
480 : : * If @field is not found or it is not a boolean, %FALSE will be returned and
481 : : * @value will not be set.
482 : : *
483 : : * Returns: %TRUE, or %FALSE on failure
484 : : *
485 : : * Since: 1.0
486 : : */
487 : : gboolean
488 : 47 : valent_packet_get_boolean (JsonNode *packet,
489 : : const char *field,
490 : : gboolean *value)
491 : : {
492 : 47 : JsonObject *root, *body;
493 : 47 : JsonNode *node;
494 : :
495 [ - + ]: 47 : g_return_val_if_fail (JSON_NODE_HOLDS_OBJECT (packet), FALSE);
496 [ + - + - ]: 47 : g_return_val_if_fail (field != NULL && *field != '\0', FALSE);
497 : :
498 : 47 : root = json_node_get_object (packet);
499 : :
500 [ + - - + ]: 47 : if G_UNLIKELY ((node = json_object_get_member (root, "body")) == NULL ||
501 : : json_node_get_node_type (node) != JSON_NODE_OBJECT)
502 : 0 : return FALSE;
503 : :
504 : 47 : body = json_node_get_object (node);
505 : :
506 [ + + - + ]: 47 : if G_UNLIKELY ((node = json_object_get_member (body, field)) == NULL ||
507 : : json_node_get_value_type (node) != G_TYPE_BOOLEAN)
508 : 16 : return FALSE;
509 : :
510 [ + - ]: 31 : if (value)
511 : 31 : *value = json_node_get_boolean (node);
512 : :
513 : : return TRUE;
514 : : }
515 : :
516 : : /**
517 : : * valent_packet_get_double:
518 : : * @packet: a KDE Connect packet
519 : : * @field: (not nullable): field name
520 : : * @value: (out) (nullable): a double
521 : : *
522 : : * Lookup @field in the body of @packet and assign it to @value.
523 : : *
524 : : * If @field is not found or it is not a double, %FALSE will be returned and
525 : : * @value will not be set.
526 : : *
527 : : * Returns: %TRUE, or %FALSE on failure
528 : : *
529 : : * Since: 1.0
530 : : */
531 : : gboolean
532 : 5 : valent_packet_get_double (JsonNode *packet,
533 : : const char *field,
534 : : double *value)
535 : : {
536 : 5 : JsonObject *root, *body;
537 : 5 : JsonNode *node;
538 : :
539 [ - + ]: 5 : g_return_val_if_fail (JSON_NODE_HOLDS_OBJECT (packet), FALSE);
540 [ + - + - ]: 5 : g_return_val_if_fail (field != NULL && *field != '\0', FALSE);
541 : :
542 : 5 : root = json_node_get_object (packet);
543 : :
544 [ + - - + ]: 5 : if G_UNLIKELY ((node = json_object_get_member (root, "body")) == NULL ||
545 : : json_node_get_node_type (node) != JSON_NODE_OBJECT)
546 : 0 : return FALSE;
547 : :
548 : 5 : body = json_node_get_object (node);
549 : :
550 [ + - - + ]: 5 : if G_UNLIKELY ((node = json_object_get_member (body, field)) == NULL ||
551 : : json_node_get_value_type (node) != G_TYPE_DOUBLE)
552 : 0 : return FALSE;
553 : :
554 [ + - ]: 5 : if (value)
555 : 5 : *value = json_node_get_double (node);
556 : :
557 : : return TRUE;
558 : : }
559 : :
560 : : /**
561 : : * valent_packet_get_int:
562 : : * @packet: a KDE Connect packet
563 : : * @field: (not nullable): field name
564 : : * @value: (out) (nullable): an int64
565 : : *
566 : : * Lookup @field in the body of @packet and assign it to @value.
567 : : *
568 : : * If @field is not found or it is not an integer, %FALSE will be returned and
569 : : * @value will not be set.
570 : : *
571 : : * Returns: %TRUE, or %FALSE on failure
572 : : *
573 : : * Since: 1.0
574 : : */
575 : : gboolean
576 : 314 : valent_packet_get_int (JsonNode *packet,
577 : : const char *field,
578 : : int64_t *value)
579 : : {
580 : 314 : JsonObject *root, *body;
581 : 314 : JsonNode *node;
582 : :
583 [ - + ]: 314 : g_return_val_if_fail (JSON_NODE_HOLDS_OBJECT (packet), FALSE);
584 [ + - + - ]: 314 : g_return_val_if_fail (field != NULL && *field != '\0', FALSE);
585 : :
586 : 314 : root = json_node_get_object (packet);
587 : :
588 [ + - - + ]: 314 : if G_UNLIKELY ((node = json_object_get_member (root, "body")) == NULL ||
589 : : json_node_get_node_type (node) != JSON_NODE_OBJECT)
590 : 0 : return FALSE;
591 : :
592 : 314 : body = json_node_get_object (node);
593 : :
594 [ + + - + ]: 314 : if G_UNLIKELY ((node = json_object_get_member (body, field)) == NULL ||
595 : : json_node_get_value_type (node) != G_TYPE_INT64)
596 : 63 : return FALSE;
597 : :
598 [ + + ]: 251 : if (value)
599 : 246 : *value = json_node_get_int (node);
600 : :
601 : : return TRUE;
602 : : }
603 : :
604 : : /**
605 : : * valent_packet_get_string:
606 : : * @packet: a KDE Connect packet
607 : : * @field: (not nullable): field name
608 : : * @value: (out) (nullable) (transfer none): a string
609 : : *
610 : : * Lookup @field in the body of @packet and assign it to @value.
611 : : *
612 : : * If @field is not found or it is not a non-empty string, %FALSE will be
613 : : * returned and @value will not be set.
614 : : *
615 : : * Returns: %TRUE, or %FALSE on failure
616 : : *
617 : : * Since: 1.0
618 : : */
619 : : gboolean
620 : 843 : valent_packet_get_string (JsonNode *packet,
621 : : const char *field,
622 : : const char **value)
623 : : {
624 : 843 : JsonObject *root, *body;
625 : 843 : JsonNode *node;
626 : 843 : const char *string;
627 : :
628 [ - + ]: 843 : g_return_val_if_fail (JSON_NODE_HOLDS_OBJECT (packet), FALSE);
629 [ + - + - ]: 843 : g_return_val_if_fail (field != NULL && *field != '\0', FALSE);
630 : :
631 : 843 : root = json_node_get_object (packet);
632 : :
633 [ + - - + ]: 843 : if G_UNLIKELY ((node = json_object_get_member (root, "body")) == NULL ||
634 : : json_node_get_node_type (node) != JSON_NODE_OBJECT)
635 : 0 : return FALSE;
636 : :
637 : 843 : body = json_node_get_object (node);
638 : :
639 [ + + - + ]: 843 : if G_UNLIKELY ((node = json_object_get_member (body, field)) == NULL ||
640 : : json_node_get_value_type (node) != G_TYPE_STRING)
641 : 44 : return FALSE;
642 : :
643 : 799 : string = json_node_get_string (node);
644 : :
645 [ - + ]: 799 : if G_UNLIKELY (*string == '\0')
646 : : return FALSE;
647 : :
648 [ + + ]: 799 : if (value)
649 : 796 : *value = string;
650 : :
651 : : return TRUE;
652 : : }
653 : :
654 : : /**
655 : : * valent_packet_get_array:
656 : : * @packet: a KDE Connect packet
657 : : * @field: (not nullable): field name
658 : : * @value: (out) (nullable) (transfer none): a `JsonArray`
659 : : *
660 : : * Lookup @field in the body of @packet and assign it to @value.
661 : : *
662 : : * If @field is not found or it is not a `JsonArray`, %FALSE will be returned and
663 : : * @value will not be set.
664 : : *
665 : : * Returns: %TRUE, or %FALSE on failure
666 : : *
667 : : * Since: 1.0
668 : : */
669 : : gboolean
670 : 26 : valent_packet_get_array (JsonNode *packet,
671 : : const char *field,
672 : : JsonArray **value)
673 : : {
674 : 26 : JsonObject *root, *body;
675 : 26 : JsonNode *node;
676 : :
677 [ - + ]: 26 : g_return_val_if_fail (JSON_NODE_HOLDS_OBJECT (packet), FALSE);
678 [ + - + - ]: 26 : g_return_val_if_fail (field != NULL && *field != '\0', FALSE);
679 : :
680 : 26 : root = json_node_get_object (packet);
681 : :
682 [ + - - + ]: 26 : if G_UNLIKELY ((node = json_object_get_member (root, "body")) == NULL ||
683 : : json_node_get_node_type (node) != JSON_NODE_OBJECT)
684 : 0 : return FALSE;
685 : :
686 : 26 : body = json_node_get_object (node);
687 : :
688 [ + + - + ]: 26 : if G_UNLIKELY ((node = json_object_get_member (body, field)) == NULL ||
689 : : json_node_get_node_type (node) != JSON_NODE_ARRAY)
690 : 9 : return FALSE;
691 : :
692 [ + - ]: 17 : if (value)
693 : 17 : *value = json_node_get_array (node);
694 : :
695 : : return TRUE;
696 : : }
697 : :
698 : : /**
699 : : * valent_packet_get_object:
700 : : * @packet: a KDE Connect packet
701 : : * @field: (not nullable): field name
702 : : * @value: (out) (nullable) (transfer none): a `JsonObject`
703 : : *
704 : : * Lookup @field in the body of @packet and assign it to @value.
705 : : *
706 : : * If @field is not found or it is not a `JsonObject`, %FALSE will be returned
707 : : * and @value will not be set.
708 : : *
709 : : * Returns: %TRUE, or %FALSE on failure
710 : : *
711 : : * Since: 1.0
712 : : */
713 : : gboolean
714 : 14 : valent_packet_get_object (JsonNode *packet,
715 : : const char *field,
716 : : JsonObject **value)
717 : : {
718 : 14 : JsonObject *root, *body;
719 : 14 : JsonNode *node;
720 : :
721 [ - + ]: 14 : g_return_val_if_fail (JSON_NODE_HOLDS_OBJECT (packet), FALSE);
722 [ + - + - ]: 14 : g_return_val_if_fail (field != NULL && *field != '\0', FALSE);
723 : :
724 : 14 : root = json_node_get_object (packet);
725 : :
726 [ + - - + ]: 14 : if G_UNLIKELY ((node = json_object_get_member (root, "body")) == NULL ||
727 : : json_node_get_node_type (node) != JSON_NODE_OBJECT)
728 : 0 : return FALSE;
729 : :
730 : 14 : body = json_node_get_object (node);
731 : :
732 [ + - - + ]: 14 : if G_UNLIKELY ((node = json_object_get_member (body, field)) == NULL ||
733 : : json_node_get_node_type (node) != JSON_NODE_OBJECT)
734 : 0 : return FALSE;
735 : :
736 [ + - ]: 14 : if (value)
737 : 14 : *value = json_node_get_object (node);
738 : :
739 : : return TRUE;
740 : : }
741 : :
742 : : /**
743 : : * valent_packet_dup_strv:
744 : : * @packet: a KDE Connect packet
745 : : * @field: (not nullable): field name
746 : : *
747 : : * Lookup @field in the body of @packet and return a newly allocated list of
748 : : * strings.
749 : : *
750 : : * If @field is not found, it is not a `JsonArray` or any of its elements are not
751 : : * strings, %NULL will be returned.
752 : : *
753 : : * Returns: (transfer full) (nullable) (array zero-terminated=1): a list of strings
754 : : *
755 : : * Since: 1.0
756 : : */
757 : : GStrv
758 : 353 : valent_packet_dup_strv (JsonNode *packet,
759 : : const char *field)
760 : : {
761 : 353 : JsonObject *root, *body;
762 : 353 : JsonNode *node;
763 : 353 : JsonArray *array;
764 : 706 : g_autoptr (GStrvBuilder) builder = NULL;
765 : 353 : unsigned int n_strings;
766 : :
767 [ - + ]: 353 : g_return_val_if_fail (JSON_NODE_HOLDS_OBJECT (packet), NULL);
768 [ + - + - ]: 353 : g_return_val_if_fail (field != NULL && *field != '\0', NULL);
769 : :
770 : 353 : root = json_node_get_object (packet);
771 [ + - - + ]: 353 : if G_UNLIKELY ((node = json_object_get_member (root, "body")) == NULL ||
772 : : json_node_get_node_type (node) != JSON_NODE_OBJECT)
773 : 0 : return NULL;
774 : :
775 : 353 : body = json_node_get_object (node);
776 [ + - - + ]: 353 : if G_UNLIKELY ((node = json_object_get_member (body, field)) == NULL ||
777 : : json_node_get_node_type (node) != JSON_NODE_ARRAY)
778 : 0 : return NULL;
779 : :
780 : 353 : builder = g_strv_builder_new ();
781 : 353 : array = json_node_get_array (node);
782 : 353 : n_strings = json_array_get_length (array);
783 [ + + ]: 1225 : for (unsigned int i = 0; i < n_strings; i++)
784 : : {
785 : 872 : JsonNode *element = json_array_get_element (array, i);
786 : :
787 [ + - ]: 872 : g_return_val_if_fail (json_node_get_value_type (element) == G_TYPE_STRING, NULL);
788 : 872 : g_strv_builder_add (builder, json_node_get_string (element));
789 : : }
790 : :
791 : 353 : return g_strv_builder_end (builder);
792 : : }
793 : :
794 : : /**
795 : : * valent_packet_validate:
796 : : * @packet: (nullable): a KDE Connect packet
797 : : * @error: (nullable): a `GError`
798 : : *
799 : : * Check if @packet is a well-formed KDE Connect packet.
800 : : *
801 : : * Returns: %TRUE if @packet is valid, or %FALSE with @error set
802 : : *
803 : : * Since: 1.0
804 : : */
805 : : gboolean
806 : 374 : valent_packet_validate (JsonNode *packet,
807 : : GError **error)
808 : : {
809 : 374 : JsonObject *root;
810 : 374 : JsonNode *node;
811 : :
812 [ + + ]: 374 : if G_UNLIKELY (packet == NULL)
813 : : {
814 : 2 : g_set_error_literal (error,
815 : : VALENT_PACKET_ERROR,
816 : : VALENT_PACKET_ERROR_INVALID_DATA,
817 : : "packet is NULL");
818 : 2 : return FALSE;
819 : : }
820 : :
821 [ + + ]: 372 : if G_UNLIKELY (!JSON_NODE_HOLDS_OBJECT (packet))
822 : : {
823 : 2 : g_set_error_literal (error,
824 : : VALENT_PACKET_ERROR,
825 : : VALENT_PACKET_ERROR_MALFORMED,
826 : : "expected the root element to be an object");
827 : 2 : return FALSE;
828 : : }
829 : :
830 : 370 : root = json_node_get_object (packet);
831 : :
832 [ + + + + ]: 370 : if G_UNLIKELY ((node = json_object_get_member (root, "type")) == NULL ||
833 : : json_node_get_value_type (node) != G_TYPE_STRING)
834 : : {
835 [ + + ]: 3 : g_set_error_literal (error,
836 : : VALENT_PACKET_ERROR,
837 : : node == NULL
838 : : ? VALENT_PACKET_ERROR_MISSING_FIELD
839 : : : VALENT_PACKET_ERROR_INVALID_FIELD,
840 : : "expected \"type\" field holding a string");
841 : 2 : return FALSE;
842 : : }
843 : :
844 [ + + + + ]: 368 : if G_UNLIKELY ((node = json_object_get_member (root, "body")) == NULL ||
845 : : json_node_get_node_type (node) != JSON_NODE_OBJECT)
846 : : {
847 [ + + ]: 3 : g_set_error_literal (error,
848 : : VALENT_PACKET_ERROR,
849 : : node == NULL
850 : : ? VALENT_PACKET_ERROR_MISSING_FIELD
851 : : : VALENT_PACKET_ERROR_INVALID_FIELD,
852 : : "expected \"body\" field holding an object");
853 : 2 : return FALSE;
854 : : }
855 : :
856 : : /* These two are optional, but have defined value types */
857 [ + + + + ]: 366 : if G_UNLIKELY ((node = json_object_get_member (root, "payloadSize")) != NULL &&
858 : : json_node_get_value_type (node) != G_TYPE_INT64)
859 : : {
860 : 1 : g_set_error_literal (error,
861 : : VALENT_PACKET_ERROR,
862 : : VALENT_PACKET_ERROR_INVALID_FIELD,
863 : : "expected \"payloadSize\" field to hold an integer");
864 : 1 : return FALSE;
865 : : }
866 : :
867 [ + + + + ]: 365 : if G_UNLIKELY ((node = json_object_get_member (root, "payloadTransferInfo")) != NULL &&
868 : : json_node_get_node_type (node) != JSON_NODE_OBJECT)
869 : : {
870 : 1 : g_set_error_literal (error,
871 : : VALENT_PACKET_ERROR,
872 : : VALENT_PACKET_ERROR_INVALID_FIELD,
873 : : "expected \"payloadTransferInfo\" field to hold an object");
874 : 1 : return FALSE;
875 : : }
876 : :
877 : : return TRUE;
878 : : }
879 : :
880 : : static void
881 : 21 : valent_packet_from_stream_task (GTask *task,
882 : : gpointer source_object,
883 : : gpointer task_data,
884 : : GCancellable *cancellable)
885 : : {
886 : 21 : GInputStream *stream = G_INPUT_STREAM (source_object);
887 : 21 : gssize max_len = (gssize)(intptr_t)task_data;
888 : 21 : g_autoptr (JsonParser) parser = NULL;
889 [ + - + + ]: 21 : g_autoptr (JsonNode) packet = NULL;
890 [ + - - + ]: 21 : g_autofree char *line = NULL;
891 : 21 : gssize count = 0;
892 : 21 : gssize size = 4096;
893 : 21 : GError *error = NULL;
894 : :
895 [ + - + - : 21 : g_assert (G_IS_INPUT_STREAM (stream));
+ - - + ]
896 : :
897 : : #ifndef __clang_analyzer__
898 [ + + ]: 21 : if (max_len < 0)
899 : 7 : max_len = G_MAXSSIZE;
900 : :
901 : 21 : line = g_malloc0 (size);
902 : :
903 : 15837 : while (TRUE)
904 : : {
905 : 15837 : gssize read = 0;
906 : :
907 [ + + ]: 15837 : if G_UNLIKELY (count == max_len)
908 : : {
909 : 1 : g_task_return_new_error (task,
910 : : G_IO_ERROR,
911 : : G_IO_ERROR_MESSAGE_TOO_LARGE,
912 : : "Packet too large");
913 : 1 : return;
914 : : }
915 : :
916 [ + + ]: 15836 : if G_UNLIKELY (count == size)
917 : : {
918 : 1 : size = MIN (size * 2, max_len);
919 : 1 : line = g_realloc (line, size);
920 : : }
921 : :
922 : 31672 : read = g_input_stream_read (stream,
923 : 15836 : line + count,
924 : : 1,
925 : : cancellable,
926 : : &error);
927 [ + + ]: 15836 : if (error != NULL)
928 : : {
929 : 1 : g_task_return_error (task, g_steal_pointer (&error));
930 : 1 : return;
931 : : }
932 : :
933 [ + + ]: 15835 : if (read > 0)
934 : 15833 : count += read;
935 [ - + ]: 2 : else if (read == 0)
936 : : break;
937 : :
938 [ + + + + ]: 15833 : if G_UNLIKELY (line[count - 1] == '\n')
939 : : break;
940 : : }
941 : :
942 : 19 : parser = json_parser_new_immutable ();
943 [ + + ]: 19 : if (!json_parser_load_from_data (parser, line, count, &error))
944 : : {
945 : 1 : g_task_return_error (task, g_steal_pointer (&error));
946 : 1 : return;
947 : : }
948 : :
949 : 18 : packet = json_parser_steal_root (parser);
950 [ + + ]: 18 : if (!valent_packet_validate (packet, &error))
951 : : {
952 : 2 : g_task_return_error (task, g_steal_pointer (&error));
953 : 2 : return;
954 : : }
955 : : #endif /* __clang_analyzer__ */
956 : :
957 : 16 : g_task_return_pointer (task,
958 : : g_steal_pointer (&packet),
959 : : (GDestroyNotify)json_node_unref);
960 : : }
961 : :
962 : : /**
963 : : * valent_packet_from_stream:
964 : : * @stream: a `GInputStream`
965 : : * @max_len: the maximum number bytes to read, or `-1` for no limit
966 : : * @cancellable: (nullable): a `GCancellable`
967 : : * @callback: (scope async): a `GAsyncReadyCallback`
968 : : * @user_data: user supplied data
969 : : *
970 : : * Read a KDE Connect packet from an input stream.
971 : : *
972 : : * If reading fails or the packet does not conform to the minimum structure of
973 : : * a KDE Connect packet, %NULL will be returned with @error set.
974 : : *
975 : : * If @max_len is greater than `-1`, then at most @max_len bytes will be read.
976 : : * If @max_len bytes are read without encountering a line-feed character, %NULL
977 : : * will be returned with @error set to %G_IO_ERROR_MESSAGE_TOO_LARGE.
978 : : *
979 : : * Call [func@Valent.packet_from_stream_finish] to get the result.
980 : : *
981 : : * Since: 1.0
982 : : */
983 : : void
984 : 21 : valent_packet_from_stream (GInputStream *stream,
985 : : gssize max_len,
986 : : GCancellable *cancellable,
987 : : GAsyncReadyCallback callback,
988 : : gpointer user_data)
989 : : {
990 : 42 : g_autoptr (GTask) task = NULL;
991 : 21 : gssize _max_len = max_len;
992 : :
993 [ + - + - : 21 : g_return_if_fail (G_IS_INPUT_STREAM (stream));
+ - - + ]
994 [ + + + - : 21 : g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- + - - ]
995 : :
996 : 21 : task = g_task_new (stream, cancellable, callback, user_data);
997 [ + - ]: 21 : g_task_set_source_tag (task, valent_packet_from_stream);
998 : 21 : g_task_set_task_data (task, (void *)(intptr_t)_max_len, NULL);
999 [ + - ]: 21 : g_task_run_in_thread (task, valent_packet_from_stream_task);
1000 : : }
1001 : :
1002 : : /**
1003 : : * valent_packet_from_stream_finish:
1004 : : * @stream: a `GInputStream`
1005 : : * @result: a `GAsyncResult`
1006 : : * @error: (nullable): a `GError`
1007 : : *
1008 : : * Finish an operation started by [func@Valent.packet_from_stream].
1009 : : *
1010 : : * Returns: (transfer full): a KDE Connect packet, or %NULL with @error set
1011 : : *
1012 : : * Since: 1.0
1013 : : */
1014 : : JsonNode *
1015 : 18 : valent_packet_from_stream_finish (GInputStream *stream,
1016 : : GAsyncResult *result,
1017 : : GError **error)
1018 : : {
1019 [ + - + - : 18 : g_return_val_if_fail (G_IS_INPUT_STREAM (stream), NULL);
+ - - + ]
1020 [ + - ]: 18 : g_return_val_if_fail (g_task_is_valid (result, stream), NULL);
1021 [ + - + - ]: 18 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1022 : :
1023 : 18 : return g_task_propagate_pointer (G_TASK (result), error);
1024 : : }
1025 : :
1026 : : static void
1027 : 16 : g_output_stream_write_all_cb (GOutputStream *stream,
1028 : : GAsyncResult *result,
1029 : : gpointer user_data)
1030 : : {
1031 : 32 : g_autoptr (GTask) task = G_TASK (g_steal_pointer (&user_data));
1032 : 16 : size_t n_written;
1033 : 16 : GError *error = NULL;
1034 : :
1035 [ - + ]: 16 : if (!g_output_stream_write_all_finish (stream, result, &n_written, &error))
1036 : : {
1037 : 0 : g_task_return_error (task, g_steal_pointer (&error));
1038 [ # # ]: 0 : return;
1039 : : }
1040 : :
1041 [ + - ]: 16 : g_task_return_boolean (task, TRUE);
1042 : : }
1043 : :
1044 : : /**
1045 : : * valent_packet_to_stream:
1046 : : * @stream: a `GOutputStream`
1047 : : * @packet: a KDE Connect packet
1048 : : * @cancellable: (nullable): a `GCancellable`
1049 : : * @callback: (scope async): a `GAsyncReadyCallback`
1050 : : * @user_data: user supplied data
1051 : : *
1052 : : * A convenience function for writing a KDE Connect packet to an output stream.
1053 : : *
1054 : : * Call [func@Valent.packet_to_stream_finish] to get the result.
1055 : : *
1056 : : * Since: 1.0
1057 : : */
1058 : : void
1059 : 16 : valent_packet_to_stream (GOutputStream *stream,
1060 : : JsonNode *packet,
1061 : : GCancellable *cancellable,
1062 : : GAsyncReadyCallback callback,
1063 : : gpointer user_data)
1064 : : {
1065 : 16 : g_autoptr (GTask) task = NULL;
1066 [ + - ]: 16 : g_autoptr (JsonGenerator) generator = NULL;
1067 : 16 : JsonObject *root;
1068 : 16 : char *packet_str = NULL;
1069 : 16 : size_t packet_len;
1070 : 16 : GError *error = NULL;
1071 : :
1072 [ + - + - : 16 : g_return_if_fail (G_IS_OUTPUT_STREAM (stream));
+ - - + ]
1073 [ + - ]: 16 : g_return_if_fail (packet != NULL);
1074 [ + + + - : 16 : g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- + - - ]
1075 : :
1076 [ - + ]: 16 : if (!valent_packet_validate (packet, &error))
1077 : : {
1078 : 0 : g_task_report_error (stream, callback, user_data,
1079 : : valent_packet_to_stream,
1080 : 0 : g_steal_pointer (&error));
1081 : 0 : return;
1082 : : }
1083 : :
1084 : : /* Timestamp the packet (UNIX Epoch ms)
1085 : : */
1086 : 16 : root = json_node_get_object (packet);
1087 : 16 : json_object_set_int_member (root, "id", valent_timestamp_ms ());
1088 : :
1089 : : /* Serialize the packet and replace the trailing NULL with an LF
1090 : : */
1091 : 16 : generator = json_generator_new ();
1092 : 16 : json_generator_set_root (generator, packet);
1093 : 16 : packet_str = json_generator_to_data (generator, &packet_len);
1094 : 16 : packet_str[packet_len++] = '\n';
1095 : :
1096 : 16 : task = g_task_new (stream, cancellable, callback, user_data);
1097 [ + - ]: 16 : g_task_set_source_tag (task, valent_packet_to_stream);
1098 : 16 : g_task_set_task_data (task, packet_str, g_free);
1099 [ + - ]: 16 : g_output_stream_write_all_async (stream,
1100 : : packet_str,
1101 : : packet_len,
1102 : : G_PRIORITY_DEFAULT,
1103 : : cancellable,
1104 : : (GAsyncReadyCallback)g_output_stream_write_all_cb,
1105 : : g_object_ref (task));
1106 : : }
1107 : :
1108 : : /**
1109 : : * valent_packet_to_stream_finish:
1110 : : * @stream: a `GInputStream`
1111 : : * @result: a `GAsyncResult`
1112 : : * @error: (nullable): a `GError`
1113 : : *
1114 : : * Finish an operation started by [func@Valent.packet_to_stream].
1115 : : *
1116 : : * Returns: %TRUE, or %FALSE with @error set
1117 : : *
1118 : : * Since: 1.0
1119 : : */
1120 : : gboolean
1121 : 13 : valent_packet_to_stream_finish (GOutputStream *stream,
1122 : : GAsyncResult *result,
1123 : : GError **error)
1124 : : {
1125 [ + - + - : 13 : g_return_val_if_fail (G_IS_OUTPUT_STREAM (stream), FALSE);
+ - - + ]
1126 [ + - ]: 13 : g_return_val_if_fail (g_task_is_valid (result, stream), FALSE);
1127 [ + - + - ]: 13 : g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1128 : :
1129 : 13 : return g_task_propagate_boolean (G_TASK (result), error);
1130 : : }
1131 : :
1132 : : /**
1133 : : * valent_packet_serialize:
1134 : : * @packet: a complete KDE Connect packet
1135 : : * @length: (out) (nullable): a location for the length
1136 : : *
1137 : : * Convenience function that updates the timestamp of a packet before returning
1138 : : * a serialized string with newline ending, ready to be written to a stream.
1139 : : *
1140 : : * Returns: (transfer full) (nullable): the serialized packet.
1141 : : *
1142 : : * Since: 1.0
1143 : : */
1144 : : char *
1145 : 315 : valent_packet_serialize (JsonNode *packet,
1146 : : size_t *length)
1147 : : {
1148 : 315 : JsonObject *root;
1149 : 630 : g_autoptr (JsonGenerator) generator = NULL;
1150 : 315 : GString *packet_str = NULL;
1151 : :
1152 [ - + ]: 315 : g_return_val_if_fail (VALENT_IS_PACKET (packet), NULL);
1153 : :
1154 : : /* Timestamp the packet (UNIX Epoch ms)
1155 : : */
1156 : 315 : root = json_node_get_object (packet);
1157 : 315 : json_object_set_int_member (root, "id", valent_timestamp_ms ());
1158 : :
1159 : : /* Serialize the packet and append a newline character
1160 : : */
1161 : 315 : generator = json_generator_new ();
1162 : 315 : json_generator_set_root (generator, packet);
1163 : :
1164 : 315 : packet_str = g_string_new ("");
1165 : 315 : json_generator_to_gstring (generator, packet_str);
1166 [ + - ]: 315 : g_string_append_c (packet_str, '\n');
1167 : :
1168 [ + + ]: 315 : if (length != NULL)
1169 : 313 : *length = packet_str->len;
1170 : :
1171 : 315 : return g_string_free (packet_str, FALSE);
1172 : : }
1173 : :
1174 : : /**
1175 : : * valent_packet_deserialize:
1176 : : * @json: a complete KDE Connect packet
1177 : : * @error: (nullable): a `GError`
1178 : : *
1179 : : * Convenience function that deserializes a KDE Connect packet from a string
1180 : : * with basic validation. If @str is empty, this function will return %NULL.
1181 : : *
1182 : : * If parsing or validation fails, @error will be set and %NULL returned.
1183 : : *
1184 : : * Returns: (transfer full) (nullable): a KDE Connect packet
1185 : : *
1186 : : * Since: 1.0
1187 : : */
1188 : : JsonNode *
1189 : 301 : valent_packet_deserialize (const char *json,
1190 : : GError **error)
1191 : : {
1192 : 602 : g_autoptr (JsonParser) parser = NULL;
1193 [ + - ]: 301 : g_autoptr (JsonNode) packet = NULL;
1194 : :
1195 [ - + ]: 301 : g_return_val_if_fail (json != NULL, NULL);
1196 [ + - + - ]: 301 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1197 : :
1198 : 301 : parser = json_parser_new_immutable ();
1199 : :
1200 [ + - ]: 301 : if (!json_parser_load_from_data (parser, json, -1, error))
1201 : : return NULL;
1202 : :
1203 [ + - ]: 301 : if ((packet = json_parser_steal_root (parser)) == NULL)
1204 : : return NULL;
1205 : :
1206 [ - + ]: 301 : if (!valent_packet_validate (packet, error))
1207 : 0 : return NULL;
1208 : :
1209 : : return g_steal_pointer (&packet);
1210 : : }
1211 : :
|