LCOV - code coverage report
Current view: top level - src/libvalent/device - valent-packet.c (source / functions) Coverage Total Hit
Test: Code Coverage Lines: 93.7 % 410 384
Test Date: 2025-04-25 03:07:01 Functions: 100.0 % 34 34
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 57.3 % 436 250

             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                 :          15 : valent_packet_new (const char *type)
      31                 :             : {
      32                 :          30 :   g_autoptr (JsonBuilder) builder = NULL;
      33                 :             : 
      34         [ -  + ]:          15 :   g_return_val_if_fail (type != NULL, NULL);
      35                 :             : 
      36                 :          15 :   builder = json_builder_new ();
      37                 :             : 
      38                 :          15 :   json_builder_begin_object (builder);
      39                 :          15 :   json_builder_set_member_name (builder, "id");
      40                 :          15 :   json_builder_add_int_value (builder, 0);
      41                 :          15 :   json_builder_set_member_name (builder, "type");
      42                 :          15 :   json_builder_add_string_value (builder, type);
      43                 :          15 :   json_builder_set_member_name (builder, "body");
      44                 :          15 :   json_builder_end_object (json_builder_begin_object (builder));
      45                 :          15 :   json_builder_end_object (builder);
      46                 :             : 
      47                 :          15 :   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                 :         163 : valent_packet_init (JsonBuilder **builder,
      77                 :             :                     const char   *type)
      78                 :             : {
      79   [ +  -  +  - ]:         163 :   g_return_if_fail (builder != NULL && *builder == NULL);
      80   [ +  -  +  - ]:         163 :   g_return_if_fail (type != NULL && *type != '\0');
      81                 :             : 
      82                 :         163 :   *builder = json_builder_new ();
      83                 :         163 :   json_builder_begin_object (*builder);
      84                 :         163 :   json_builder_set_member_name (*builder, "id");
      85                 :         163 :   json_builder_add_int_value (*builder, 0);
      86                 :         163 :   json_builder_set_member_name (*builder, "type");
      87                 :         163 :   json_builder_add_string_value (*builder, type);
      88                 :         163 :   json_builder_set_member_name (*builder, "body");
      89                 :             : 
      90                 :         163 :   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                 :         163 : valent_packet_end (JsonBuilder **builder)
     109                 :             : {
     110                 :         163 :   JsonNode *ret = NULL;
     111                 :             : 
     112   [ +  -  +  -  :         163 :   g_return_val_if_fail (builder != NULL && JSON_IS_BUILDER (*builder), NULL);
          +  -  -  +  -  
                      - ]
     113                 :             : 
     114                 :             :   /* Finish the `body` object and the root object */
     115                 :         163 :   json_builder_end_object (*builder);
     116                 :         163 :   json_builder_end_object (*builder);
     117                 :             : 
     118                 :         163 :   ret = json_builder_get_root (*builder);
     119         [ +  - ]:         163 :   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                 :         292 : valent_packet_get_type (JsonNode *packet)
     163                 :             : {
     164                 :         292 :   JsonObject *root;
     165                 :         292 :   JsonNode *node;
     166                 :             : 
     167         [ -  + ]:         292 :   g_return_val_if_fail (JSON_NODE_HOLDS_OBJECT (packet), NULL);
     168                 :             : 
     169                 :         292 :   root = json_node_get_object (packet);
     170                 :             : 
     171   [ +  -  -  + ]:         292 :   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                 :         292 :   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                 :         597 : valent_packet_get_body (JsonNode *packet)
     190                 :             : {
     191                 :         597 :   JsonObject *root;
     192                 :         597 :   JsonNode *node;
     193                 :             : 
     194         [ -  + ]:         597 :   g_return_val_if_fail (JSON_NODE_HOLDS_OBJECT (packet), NULL);
     195                 :             : 
     196                 :         597 :   root = json_node_get_object (packet);
     197                 :             : 
     198   [ +  -  -  + ]:         597 :   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                 :         597 :   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                 :          44 : valent_packet_has_payload (JsonNode *packet)
     222                 :             : {
     223                 :          44 :   JsonObject *root;
     224                 :          44 :   JsonNode *node;
     225                 :             : 
     226         [ -  + ]:          44 :   g_return_val_if_fail (VALENT_IS_PACKET (packet), FALSE);
     227                 :             : 
     228                 :          44 :   root = json_node_get_object (packet);
     229                 :             : 
     230   [ +  +  -  + ]:          81 :   if ((node = json_object_get_member (root, "payloadSize")) != NULL &&
     231                 :          37 :       json_node_get_value_type (node) != G_TYPE_INT64)
     232                 :             :     return FALSE;
     233                 :             : 
     234   [ +  +  -  + ]:          71 :   if ((node = json_object_get_member (root, "payloadTransferInfo")) == NULL ||
     235                 :          27 :       json_node_get_node_type (node) != JSON_NODE_OBJECT)
     236                 :          17 :     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                 :          29 : valent_packet_get_payload_full (JsonNode  *packet,
     260                 :             :                                 goffset   *size,
     261                 :             :                                 GError   **error)
     262                 :             : {
     263                 :          29 :   JsonObject *root;
     264                 :          29 :   JsonNode *node;
     265                 :             : 
     266         [ -  + ]:          29 :   if (!valent_packet_validate (packet, error))
     267                 :             :     return NULL;
     268                 :             : 
     269                 :          29 :   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   [ +  -  -  + ]:          58 :   if ((node = json_object_get_member (root, "payloadSize")) != NULL &&
     274                 :          29 :       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         [ +  - ]:          29 :   if (size != NULL)
     284         [ +  - ]:          29 :     *size = node ? json_node_get_int (node) : -1;
     285                 :             : 
     286   [ +  -  -  + ]:          58 :   if ((node = json_object_get_member (root, "payloadTransferInfo")) == NULL ||
     287                 :          29 :       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                 :          29 :   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                 :          29 : valent_packet_set_payload_info (JsonNode   *packet,
     364                 :             :                                 JsonObject *info)
     365                 :             : {
     366                 :          29 :   JsonObject *root;
     367                 :             : 
     368         [ -  + ]:          29 :   g_return_if_fail (VALENT_IS_PACKET (packet));
     369         [ +  - ]:          29 :   g_return_if_fail (info != NULL);
     370                 :             : 
     371                 :          29 :   root = json_node_get_object (packet);
     372                 :             : 
     373                 :          29 :   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                 :          44 : valent_packet_get_payload_size (JsonNode *packet)
     388                 :             : {
     389                 :          44 :   JsonObject *root;
     390                 :          44 :   JsonNode *node;
     391                 :             : 
     392         [ -  + ]:          44 :   g_return_val_if_fail (VALENT_IS_PACKET (packet), 0);
     393                 :             : 
     394                 :          44 :   root = json_node_get_object (packet);
     395                 :          44 :   node = json_object_get_member (root, "payloadSize");
     396                 :             : 
     397   [ +  -  -  + ]:          88 :   if ((node = json_object_get_member (root, "payloadSize")) != NULL &&
     398                 :          44 :       json_node_get_value_type (node) != G_TYPE_INT64)
     399                 :           0 :     g_return_val_if_reached (-1);
     400                 :             : 
     401                 :          44 :   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                 :          38 : valent_packet_set_payload_size (JsonNode *packet,
     415                 :             :                                 goffset   size)
     416                 :             : {
     417                 :          38 :   JsonObject *root;
     418                 :             : 
     419         [ -  + ]:          38 :   g_return_if_fail (VALENT_IS_PACKET (packet));
     420         [ +  - ]:          38 :   g_return_if_fail (size >= -1);
     421                 :             : 
     422                 :          38 :   root = json_node_get_object (packet);
     423                 :             : 
     424                 :          38 :   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                 :         128 : valent_packet_check_field (JsonNode   *packet,
     443                 :             :                            const char *field)
     444                 :             : {
     445                 :         128 :   JsonObject *root;
     446                 :         128 :   JsonObject *body;
     447                 :         128 :   JsonNode *node;
     448                 :             : 
     449         [ -  + ]:         128 :   g_return_val_if_fail (JSON_NODE_HOLDS_OBJECT (packet), FALSE);
     450   [ +  -  +  - ]:         128 :   g_return_val_if_fail (field != NULL && *field != '\0', FALSE);
     451                 :             : 
     452                 :         128 :   root = json_node_get_object (packet);
     453                 :             : 
     454   [ +  -  -  + ]:         128 :   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                 :         128 :   body = json_node_get_object (node);
     459                 :             : 
     460         [ +  + ]:         128 :   if G_UNLIKELY ((node = json_object_get_member (body, field)) == NULL)
     461                 :             :     return FALSE;
     462                 :             : 
     463         [ +  + ]:          47 :   if (json_node_get_value_type (node) == G_TYPE_BOOLEAN)
     464                 :          34 :     return json_node_get_boolean (node);
     465                 :             : 
     466         [ +  - ]:          13 :   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                 :         312 : valent_packet_get_int (JsonNode   *packet,
     577                 :             :                        const char *field,
     578                 :             :                        int64_t    *value)
     579                 :             : {
     580                 :         312 :   JsonObject *root, *body;
     581                 :         312 :   JsonNode *node;
     582                 :             : 
     583         [ -  + ]:         312 :   g_return_val_if_fail (JSON_NODE_HOLDS_OBJECT (packet), FALSE);
     584   [ +  -  +  - ]:         312 :   g_return_val_if_fail (field != NULL && *field != '\0', FALSE);
     585                 :             : 
     586                 :         312 :   root = json_node_get_object (packet);
     587                 :             : 
     588   [ +  -  -  + ]:         312 :   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                 :         312 :   body = json_node_get_object (node);
     593                 :             : 
     594   [ +  +  -  + ]:         312 :   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         [ +  + ]:         249 :   if (value)
     599                 :         244 :     *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): 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                 :         825 : valent_packet_get_string (JsonNode    *packet,
     621                 :             :                           const char  *field,
     622                 :             :                           const char **value)
     623                 :             : {
     624                 :         825 :   JsonObject *root, *body;
     625                 :         825 :   JsonNode *node;
     626                 :         825 :   const char *string;
     627                 :             : 
     628         [ -  + ]:         825 :   g_return_val_if_fail (JSON_NODE_HOLDS_OBJECT (packet), FALSE);
     629   [ +  -  +  - ]:         825 :   g_return_val_if_fail (field != NULL && *field != '\0', FALSE);
     630                 :             : 
     631                 :         825 :   root = json_node_get_object (packet);
     632                 :             : 
     633   [ +  -  -  + ]:         825 :   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                 :         825 :   body = json_node_get_object (node);
     638                 :             : 
     639   [ +  +  -  + ]:         825 :   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                 :         781 :   string = json_node_get_string (node);
     644                 :             : 
     645         [ -  + ]:         781 :   if G_UNLIKELY (*string == '\0')
     646                 :             :     return FALSE;
     647                 :             : 
     648         [ +  + ]:         781 :   if (value)
     649                 :         778 :     *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): 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): 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_auto (GStrv) strv = 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                 :             : #ifndef __clang_analyzer__
     771                 :         353 :   root = json_node_get_object (packet);
     772                 :             : 
     773   [ +  -  -  + ]:         353 :   if G_UNLIKELY ((node = json_object_get_member (root, "body")) == NULL ||
     774                 :             :                  json_node_get_node_type (node) != JSON_NODE_OBJECT)
     775                 :           0 :     return NULL;
     776                 :             : 
     777                 :         353 :   body = json_node_get_object (node);
     778                 :             : 
     779   [ +  -  -  + ]:         353 :   if G_UNLIKELY ((node = json_object_get_member (body, field)) == NULL ||
     780                 :             :                  json_node_get_node_type (node) != JSON_NODE_ARRAY)
     781                 :           0 :     return NULL;
     782                 :             : 
     783                 :         353 :   array = json_node_get_array (node);
     784                 :         353 :   n_strings = json_array_get_length (array);
     785         [ -  + ]:         353 :   strv = g_new0 (char *, n_strings + 1);
     786                 :             : 
     787         [ +  + ]:        1081 :   for (unsigned int i = 0; i < n_strings; i++)
     788                 :             :     {
     789                 :         728 :       JsonNode *element = json_array_get_element (array, i);
     790                 :             : 
     791         [ +  - ]:         728 :       if G_UNLIKELY (json_node_get_value_type (element) != G_TYPE_STRING)
     792                 :             :         return NULL;
     793                 :             : 
     794                 :         728 :       strv[i] = json_node_dup_string (element);
     795                 :             :     }
     796                 :             : #endif /* __clang_analyzer__ */
     797                 :             : 
     798                 :             :   return g_steal_pointer (&strv);
     799                 :             : }
     800                 :             : 
     801                 :             : /**
     802                 :             :  * valent_packet_validate:
     803                 :             :  * @packet: (nullable): a KDE Connect packet
     804                 :             :  * @error: (nullable): a `GError`
     805                 :             :  *
     806                 :             :  * Check if @packet is a well-formed KDE Connect packet.
     807                 :             :  *
     808                 :             :  * Returns: %TRUE if @packet is valid, or %FALSE with @error set
     809                 :             :  *
     810                 :             :  * Since: 1.0
     811                 :             :  */
     812                 :             : gboolean
     813                 :         380 : valent_packet_validate (JsonNode  *packet,
     814                 :             :                         GError   **error)
     815                 :             : {
     816                 :         380 :   JsonObject *root;
     817                 :         380 :   JsonNode *node;
     818                 :             : 
     819         [ +  + ]:         380 :   if G_UNLIKELY (packet == NULL)
     820                 :             :     {
     821                 :           2 :       g_set_error_literal (error,
     822                 :             :                            VALENT_PACKET_ERROR,
     823                 :             :                            VALENT_PACKET_ERROR_INVALID_DATA,
     824                 :             :                            "packet is NULL");
     825                 :           2 :       return FALSE;
     826                 :             :     }
     827                 :             : 
     828         [ +  + ]:         378 :   if G_UNLIKELY (!JSON_NODE_HOLDS_OBJECT (packet))
     829                 :             :     {
     830                 :           2 :       g_set_error_literal (error,
     831                 :             :                            VALENT_PACKET_ERROR,
     832                 :             :                            VALENT_PACKET_ERROR_MALFORMED,
     833                 :             :                            "expected the root element to be an object");
     834                 :           2 :       return FALSE;
     835                 :             :     }
     836                 :             : 
     837                 :         376 :   root = json_node_get_object (packet);
     838                 :             : 
     839   [ +  +  +  + ]:         376 :   if G_UNLIKELY ((node = json_object_get_member (root, "type")) == NULL ||
     840                 :             :                  json_node_get_value_type (node) != G_TYPE_STRING)
     841                 :             :     {
     842         [ +  + ]:           3 :       g_set_error_literal (error,
     843                 :             :                            VALENT_PACKET_ERROR,
     844                 :             :                            node == NULL
     845                 :             :                              ? VALENT_PACKET_ERROR_MISSING_FIELD
     846                 :             :                              : VALENT_PACKET_ERROR_INVALID_FIELD,
     847                 :             :                            "expected \"type\" field holding a string");
     848                 :           2 :       return FALSE;
     849                 :             :     }
     850                 :             : 
     851   [ +  +  +  + ]:         374 :   if G_UNLIKELY ((node = json_object_get_member (root, "body")) == NULL ||
     852                 :             :                  json_node_get_node_type (node) != JSON_NODE_OBJECT)
     853                 :             :     {
     854         [ +  + ]:           3 :       g_set_error_literal (error,
     855                 :             :                            VALENT_PACKET_ERROR,
     856                 :             :                            node == NULL
     857                 :             :                              ? VALENT_PACKET_ERROR_MISSING_FIELD
     858                 :             :                              : VALENT_PACKET_ERROR_INVALID_FIELD,
     859                 :             :                            "expected \"body\" field holding an object");
     860                 :           2 :       return FALSE;
     861                 :             :     }
     862                 :             : 
     863                 :             :   /* These two are optional, but have defined value types */
     864   [ +  +  +  + ]:         372 :   if G_UNLIKELY ((node = json_object_get_member (root, "payloadSize")) != NULL &&
     865                 :             :                  json_node_get_value_type (node) != G_TYPE_INT64)
     866                 :             :     {
     867                 :           1 :       g_set_error_literal (error,
     868                 :             :                            VALENT_PACKET_ERROR,
     869                 :             :                            VALENT_PACKET_ERROR_INVALID_FIELD,
     870                 :             :                            "expected \"payloadSize\" field to hold an integer");
     871                 :           1 :       return FALSE;
     872                 :             :     }
     873                 :             : 
     874   [ +  +  +  + ]:         371 :   if G_UNLIKELY ((node = json_object_get_member (root, "payloadTransferInfo")) != NULL &&
     875                 :             :                  json_node_get_node_type (node) != JSON_NODE_OBJECT)
     876                 :             :     {
     877                 :           1 :       g_set_error_literal (error,
     878                 :             :                            VALENT_PACKET_ERROR,
     879                 :             :                            VALENT_PACKET_ERROR_INVALID_FIELD,
     880                 :             :                            "expected \"payloadTransferInfo\" field to hold an object");
     881                 :           1 :       return FALSE;
     882                 :             :     }
     883                 :             : 
     884                 :             :   return TRUE;
     885                 :             : }
     886                 :             : 
     887                 :             : static void
     888                 :          21 : valent_packet_from_stream_task (GTask        *task,
     889                 :             :                                 gpointer      source_object,
     890                 :             :                                 gpointer      task_data,
     891                 :             :                                 GCancellable *cancellable)
     892                 :             : {
     893                 :          21 :   GInputStream *stream = G_INPUT_STREAM (source_object);
     894                 :          21 :   gssize max_len = (gssize)(intptr_t)task_data;
     895                 :          21 :   g_autoptr (JsonParser) parser = NULL;
     896   [ +  -  +  + ]:          21 :   g_autoptr (JsonNode) packet = NULL;
     897   [ +  -  -  + ]:          21 :   g_autofree char *line = NULL;
     898                 :          21 :   gssize count = 0;
     899                 :          21 :   gssize size = 4096;
     900                 :          21 :   GError *error = NULL;
     901                 :             : 
     902   [ +  -  +  -  :          21 :   g_assert (G_IS_INPUT_STREAM (stream));
             +  -  -  + ]
     903                 :             : 
     904                 :             : #ifndef __clang_analyzer__
     905         [ +  + ]:          21 :   if (max_len < 0)
     906                 :          11 :     max_len = G_MAXSSIZE;
     907                 :             : 
     908                 :          21 :   line = g_malloc0 (size);
     909                 :             : 
     910                 :        8887 :   while (TRUE)
     911                 :             :     {
     912                 :        8887 :       gssize read = 0;
     913                 :             : 
     914         [ +  + ]:        8887 :       if G_UNLIKELY (count == max_len)
     915                 :             :         {
     916                 :           1 :           g_task_return_new_error (task,
     917                 :             :                                    G_IO_ERROR,
     918                 :             :                                    G_IO_ERROR_MESSAGE_TOO_LARGE,
     919                 :             :                                    "Packet too large");
     920                 :           1 :           return;
     921                 :             :         }
     922                 :             : 
     923         [ +  + ]:        8886 :       if G_UNLIKELY (count == size)
     924                 :             :         {
     925                 :           1 :           size = MIN (size * 2, max_len);
     926                 :           1 :           line = g_realloc (line, size);
     927                 :             :         }
     928                 :             : 
     929                 :       17772 :       read = g_input_stream_read (stream,
     930                 :        8886 :                                   line + count,
     931                 :             :                                   1,
     932                 :             :                                   cancellable,
     933                 :             :                                   &error);
     934         [ +  + ]:        8886 :       if (error != NULL)
     935                 :             :         {
     936                 :           1 :           g_task_return_error (task, g_steal_pointer (&error));
     937                 :           1 :           return;
     938                 :             :         }
     939                 :             : 
     940         [ +  + ]:        8885 :       if (read > 0)
     941                 :        8883 :         count += read;
     942         [ -  + ]:           2 :       else if (read == 0)
     943                 :             :         break;
     944                 :             : 
     945   [ +  +  +  + ]:        8883 :       if G_UNLIKELY (line[count - 1] == '\n')
     946                 :             :         break;
     947                 :             :     }
     948                 :             : 
     949                 :          19 :   parser = json_parser_new_immutable ();
     950         [ +  + ]:          19 :   if (!json_parser_load_from_data (parser, line, count, &error))
     951                 :             :     {
     952                 :           1 :       g_task_return_error (task, g_steal_pointer (&error));
     953                 :           1 :       return;
     954                 :             :     }
     955                 :             : 
     956                 :          18 :   packet = json_parser_steal_root (parser);
     957         [ +  + ]:          18 :   if (!valent_packet_validate (packet, &error))
     958                 :             :     {
     959                 :           2 :       g_task_return_error (task, g_steal_pointer (&error));
     960                 :           2 :       return;
     961                 :             :     }
     962                 :             : #endif /* __clang_analyzer__ */
     963                 :             : 
     964                 :          16 :   g_task_return_pointer (task,
     965                 :             :                          g_steal_pointer (&packet),
     966                 :             :                          (GDestroyNotify)json_node_unref);
     967                 :             : }
     968                 :             : 
     969                 :             : /**
     970                 :             :  * valent_packet_from_stream_async:
     971                 :             :  * @stream: a `GInputStream`
     972                 :             :  * @max_len: the maximum number bytes to read, or `-1` for no limit
     973                 :             :  * @cancellable: (nullable): a `GCancellable`
     974                 :             :  * @callback: (scope async): a `GAsyncReadyCallback`
     975                 :             :  * @user_data: user supplied data
     976                 :             :  *
     977                 :             :  * Read a KDE Connect packet from an input stream.
     978                 :             :  *
     979                 :             :  * If reading fails or the packet does not conform to the minimum structure of
     980                 :             :  * a KDE Connect packet, %NULL will be returned with @error set.
     981                 :             :  *
     982                 :             :  * If @max_len is greater than `-1`, then at most @max_len bytes will be read.
     983                 :             :  * If @max_len bytes are read without encountering a line-feed character, %NULL
     984                 :             :  * will be returned with @error set to %G_IO_ERROR_MESSAGE_TOO_LARGE.
     985                 :             :  *
     986                 :             :  * Call [func@Valent.packet_from_stream_finish] to get the result.
     987                 :             :  *
     988                 :             :  * Since: 1.0
     989                 :             :  */
     990                 :             : void
     991                 :          21 : valent_packet_from_stream_async (GInputStream        *stream,
     992                 :             :                                  gssize               max_len,
     993                 :             :                                  GCancellable        *cancellable,
     994                 :             :                                  GAsyncReadyCallback  callback,
     995                 :             :                                  gpointer             user_data)
     996                 :             : {
     997                 :          42 :   g_autoptr (GTask) task = NULL;
     998                 :          21 :   gssize _max_len = max_len;
     999                 :             : 
    1000   [ +  -  +  -  :          21 :   g_return_if_fail (G_IS_INPUT_STREAM (stream));
             +  -  -  + ]
    1001   [ +  +  +  -  :          21 :   g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
             -  +  -  - ]
    1002                 :             : 
    1003                 :          21 :   task = g_task_new (stream, cancellable, callback, user_data);
    1004         [ +  - ]:          21 :   g_task_set_source_tag (task, valent_packet_from_stream_async);
    1005                 :          21 :   g_task_set_task_data (task, (void *)(intptr_t)_max_len, NULL);
    1006         [ +  - ]:          21 :   g_task_run_in_thread (task, valent_packet_from_stream_task);
    1007                 :             : }
    1008                 :             : 
    1009                 :             : /**
    1010                 :             :  * valent_packet_from_stream_finish:
    1011                 :             :  * @stream: a `GInputStream`
    1012                 :             :  * @result: a `GAsyncResult`
    1013                 :             :  * @error: (nullable): a `GError`
    1014                 :             :  *
    1015                 :             :  * Finish an operation started by [func@Valent.packet_from_stream].
    1016                 :             :  *
    1017                 :             :  * Returns: (transfer full): a KDE Connect packet, or %NULL with @error set
    1018                 :             :  *
    1019                 :             :  * Since: 1.0
    1020                 :             :  */
    1021                 :             : JsonNode *
    1022                 :          21 : valent_packet_from_stream_finish (GInputStream  *stream,
    1023                 :             :                                   GAsyncResult  *result,
    1024                 :             :                                   GError       **error)
    1025                 :             : {
    1026   [ +  -  +  -  :          21 :   g_return_val_if_fail (G_IS_INPUT_STREAM (stream), NULL);
             +  -  -  + ]
    1027         [ +  - ]:          21 :   g_return_val_if_fail (g_task_is_valid (result, stream), NULL);
    1028   [ +  -  +  - ]:          21 :   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
    1029                 :             : 
    1030                 :          21 :   return g_task_propagate_pointer (G_TASK (result), error);
    1031                 :             : }
    1032                 :             : 
    1033                 :             : static void
    1034                 :          15 : valent_packet_from_stream_cb (GObject      *object,
    1035                 :             :                               GAsyncResult *result,
    1036                 :             :                               gpointer      user_data)
    1037                 :             : {
    1038                 :          15 :   GInputStream *stream = G_INPUT_STREAM (object);
    1039                 :          15 :   GTask *task = G_TASK (user_data);
    1040                 :          15 :   JsonNode *packet;
    1041                 :          15 :   GError *error = NULL;
    1042                 :             : 
    1043                 :          15 :   packet = valent_packet_from_stream_finish (stream, result, &error);
    1044         [ +  + ]:          15 :   if (packet != NULL)
    1045                 :             :     {
    1046                 :          10 :       g_task_return_pointer (task,
    1047                 :             :                              g_steal_pointer (&packet),
    1048                 :             :                              (GDestroyNotify)json_node_unref);
    1049                 :             :     }
    1050                 :             :   else
    1051                 :             :     {
    1052                 :           5 :       g_task_return_error (task, g_steal_pointer (&error));
    1053                 :             :     }
    1054                 :          15 : }
    1055                 :             : 
    1056                 :             : /**
    1057                 :             :  * valent_packet_from_stream:
    1058                 :             :  * @stream: a `GInputStream`
    1059                 :             :  * @max_len: the maximum number bytes to read, or `-1` for no limit
    1060                 :             :  * @cancellable: (nullable): a `GCancellable`
    1061                 :             :  * @error: (nullable): a `GError`
    1062                 :             :  *
    1063                 :             :  * Read a KDE Connect packet from an input stream.
    1064                 :             :  *
    1065                 :             :  * This is a synchronous wrapper around [func@Valent.packet_from_stream_async]
    1066                 :             :  * that iterates the thread-default main context, as returned by
    1067                 :             :  * [func@GLib.MainContext.ref_thread_default].
    1068                 :             :  *
    1069                 :             :  * Returns: (transfer full): a KDE Connect packet, or %NULL with @error set.
    1070                 :             :  *
    1071                 :             :  * Since: 1.0
    1072                 :             :  */
    1073                 :             : JsonNode *
    1074                 :          15 : valent_packet_from_stream (GInputStream  *stream,
    1075                 :             :                            gssize         max_len,
    1076                 :             :                            GCancellable  *cancellable,
    1077                 :             :                            GError       **error)
    1078                 :             : {
    1079                 :          30 :   g_autoptr (GMainContext) context = NULL;
    1080         [ +  - ]:          15 :   g_autoptr (GTask) task = NULL;
    1081                 :             : 
    1082   [ +  -  +  -  :          15 :   g_return_val_if_fail (G_IS_INPUT_STREAM (stream), NULL);
             +  -  -  + ]
    1083   [ +  +  +  -  :          15 :   g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
             -  +  -  - ]
    1084   [ +  -  +  - ]:          15 :   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
    1085                 :             : 
    1086                 :          15 :   task = g_task_new (NULL, NULL, NULL, NULL);
    1087         [ +  - ]:          15 :   g_task_set_source_tag (task, valent_packet_from_stream);
    1088                 :             : 
    1089                 :          15 :   valent_packet_from_stream_async (stream,
    1090                 :             :                                    max_len,
    1091                 :             :                                    cancellable,
    1092                 :             :                                    (GAsyncReadyCallback)valent_packet_from_stream_cb,
    1093                 :             :                                    task);
    1094                 :             : 
    1095                 :          15 :   context = g_main_context_ref_thread_default ();
    1096         [ +  + ]:        5968 :   while (!g_task_get_completed (task))
    1097                 :        5953 :     g_main_context_iteration (context, TRUE);
    1098                 :             : 
    1099                 :          15 :   return g_task_propagate_pointer (task, error);
    1100                 :             : }
    1101                 :             : 
    1102                 :             : static void
    1103                 :           6 : g_output_stream_write_all_cb (GOutputStream *stream,
    1104                 :             :                               GAsyncResult  *result,
    1105                 :             :                               gpointer       user_data)
    1106                 :             : {
    1107                 :          12 :   g_autoptr (GTask) task = G_TASK (g_steal_pointer (&user_data));
    1108                 :           6 :   size_t n_written;
    1109                 :           6 :   GError *error = NULL;
    1110                 :             : 
    1111         [ -  + ]:           6 :   if (!g_output_stream_write_all_finish (stream, result, &n_written, &error))
    1112                 :             :     {
    1113                 :           0 :       g_task_return_error (task, g_steal_pointer (&error));
    1114         [ #  # ]:           0 :       return;
    1115                 :             :     }
    1116                 :             : 
    1117         [ +  - ]:           6 :   g_task_return_boolean (task, TRUE);
    1118                 :             : }
    1119                 :             : 
    1120                 :             : /**
    1121                 :             :  * valent_packet_to_stream_async:
    1122                 :             :  * @stream: a `GOutputStream`
    1123                 :             :  * @packet: a KDE Connect packet
    1124                 :             :  * @cancellable: (nullable): a `GCancellable`
    1125                 :             :  * @callback: (scope async): a `GAsyncReadyCallback`
    1126                 :             :  * @user_data: user supplied data
    1127                 :             :  *
    1128                 :             :  * A convenience function for writing a KDE Connect packet to an output stream.
    1129                 :             :  *
    1130                 :             :  * Call [func@Valent.packet_to_stream_finish] to get the result.
    1131                 :             :  *
    1132                 :             :  * Since: 1.0
    1133                 :             :  */
    1134                 :             : void
    1135                 :           6 : valent_packet_to_stream_async (GOutputStream       *stream,
    1136                 :             :                                JsonNode            *packet,
    1137                 :             :                                GCancellable        *cancellable,
    1138                 :             :                                GAsyncReadyCallback  callback,
    1139                 :             :                                gpointer             user_data)
    1140                 :             : {
    1141                 :           6 :   g_autoptr (GTask) task = NULL;
    1142         [ +  - ]:           6 :   g_autoptr (JsonGenerator) generator = NULL;
    1143                 :           6 :   JsonObject *root;
    1144         [ +  - ]:           6 :   g_autofree char *packet_str = NULL;
    1145                 :           6 :   size_t packet_len;
    1146                 :           6 :   GError *error = NULL;
    1147                 :             : 
    1148   [ +  -  +  -  :           6 :   g_return_if_fail (G_IS_OUTPUT_STREAM (stream));
             +  -  -  + ]
    1149         [ +  - ]:           6 :   g_return_if_fail (packet != NULL);
    1150   [ -  +  -  -  :           6 :   g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
             -  -  -  - ]
    1151                 :             : 
    1152         [ -  + ]:           6 :   if (!valent_packet_validate (packet, &error))
    1153                 :             :     {
    1154                 :           0 :       g_task_report_error (stream, callback, user_data,
    1155                 :             :                            valent_packet_to_stream_async,
    1156                 :           0 :                            g_steal_pointer (&error));
    1157                 :           0 :       return;
    1158                 :             :     }
    1159                 :             : 
    1160                 :             :   /* Timestamp the packet (UNIX Epoch ms)
    1161                 :             :    */
    1162                 :           6 :   root = json_node_get_object (packet);
    1163                 :           6 :   json_object_set_int_member (root, "id", valent_timestamp_ms ());
    1164                 :             : 
    1165                 :             :   /* Serialize the packet and replace the trailing NULL with an LF
    1166                 :             :    */
    1167                 :           6 :   generator = json_generator_new ();
    1168                 :           6 :   json_generator_set_root (generator, packet);
    1169                 :           6 :   packet_str = json_generator_to_data (generator, &packet_len);
    1170                 :           6 :   packet_str[packet_len++] = '\n';
    1171                 :             : 
    1172                 :           6 :   task = g_task_new (stream, cancellable, callback, user_data);
    1173         [ +  - ]:           6 :   g_task_set_source_tag (task, valent_packet_to_stream_async);
    1174                 :           6 :   g_output_stream_write_all_async (stream,
    1175                 :             :                                    packet_str,
    1176                 :             :                                    packet_len,
    1177                 :             :                                    G_PRIORITY_DEFAULT,
    1178                 :             :                                    cancellable,
    1179                 :             :                                    (GAsyncReadyCallback)g_output_stream_write_all_cb,
    1180                 :             :                                    g_object_ref (task));
    1181                 :             : }
    1182                 :             : 
    1183                 :             : /**
    1184                 :             :  * valent_packet_to_stream_finish:
    1185                 :             :  * @stream: a `GInputStream`
    1186                 :             :  * @result: a `GAsyncResult`
    1187                 :             :  * @error: (nullable): a `GError`
    1188                 :             :  *
    1189                 :             :  * Finish an operation started by [func@Valent.packet_to_stream_async].
    1190                 :             :  *
    1191                 :             :  * Returns: %TRUE, or %FALSE with @error set
    1192                 :             :  *
    1193                 :             :  * Since: 1.0
    1194                 :             :  */
    1195                 :             : gboolean
    1196                 :           6 : valent_packet_to_stream_finish (GOutputStream  *stream,
    1197                 :             :                                 GAsyncResult   *result,
    1198                 :             :                                 GError        **error)
    1199                 :             : {
    1200   [ +  -  +  -  :           6 :   g_return_val_if_fail (G_IS_OUTPUT_STREAM (stream), FALSE);
             +  -  -  + ]
    1201         [ +  - ]:           6 :   g_return_val_if_fail (g_task_is_valid (result, stream), FALSE);
    1202   [ +  -  +  - ]:           6 :   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
    1203                 :             : 
    1204                 :           6 :   return g_task_propagate_boolean (G_TASK (result), error);
    1205                 :             : }
    1206                 :             : 
    1207                 :             : /**
    1208                 :             :  * valent_packet_to_stream:
    1209                 :             :  * @stream: a `GOutputStream`
    1210                 :             :  * @packet: a KDE Connect packet
    1211                 :             :  * @cancellable: (nullable): a `GCancellable`
    1212                 :             :  * @error: (nullable): a `GError`
    1213                 :             :  *
    1214                 :             :  * Write a KDE Connect packet to an output stream.
    1215                 :             :  *
    1216                 :             :  * Returns: %TRUE if successful, or %FALSE with @error set
    1217                 :             :  *
    1218                 :             :  * Since: 1.0
    1219                 :             :  */
    1220                 :             : gboolean
    1221                 :          10 : valent_packet_to_stream (GOutputStream  *stream,
    1222                 :             :                          JsonNode       *packet,
    1223                 :             :                          GCancellable   *cancellable,
    1224                 :             :                          GError        **error)
    1225                 :             : {
    1226                 :          20 :   g_autoptr (JsonGenerator) generator = NULL;
    1227                 :          10 :   JsonObject *root;
    1228         [ +  - ]:          10 :   g_autofree char *packet_str = NULL;
    1229                 :          10 :   size_t packet_len;
    1230                 :             : 
    1231   [ +  -  +  -  :          10 :   g_return_val_if_fail (G_IS_OUTPUT_STREAM (stream), FALSE);
             +  -  -  + ]
    1232         [ +  - ]:          10 :   g_return_val_if_fail (packet != NULL, FALSE);
    1233   [ +  +  +  -  :          10 :   g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
             -  +  -  - ]
    1234   [ +  -  +  - ]:          10 :   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
    1235                 :             : 
    1236         [ -  + ]:          10 :   if (!valent_packet_validate (packet, error))
    1237                 :             :     return FALSE;
    1238                 :             : 
    1239                 :             :   /* Timestamp the packet (UNIX Epoch ms)
    1240                 :             :    */
    1241                 :          10 :   root = json_node_get_object (packet);
    1242                 :          10 :   json_object_set_int_member (root, "id", valent_timestamp_ms ());
    1243                 :             : 
    1244                 :             :   /* Serialize the packet and replace the trailing NULL with an LF
    1245                 :             :    */
    1246                 :          10 :   generator = json_generator_new ();
    1247                 :          10 :   json_generator_set_root (generator, packet);
    1248                 :          10 :   packet_str = json_generator_to_data (generator, &packet_len);
    1249                 :          10 :   packet_str[packet_len++] = '\n';
    1250                 :             : 
    1251                 :          10 :   return g_output_stream_write_all (stream,
    1252                 :             :                                     packet_str,
    1253                 :             :                                     packet_len,
    1254                 :             :                                     NULL,
    1255                 :             :                                     cancellable,
    1256                 :             :                                     error);
    1257                 :             : }
    1258                 :             : 
    1259                 :             : /**
    1260                 :             :  * valent_packet_serialize:
    1261                 :             :  * @packet: a complete KDE Connect packet
    1262                 :             :  * @length: (out) (nullable): a location for the length
    1263                 :             :  *
    1264                 :             :  * Convenience function that updates the timestamp of a packet before returning
    1265                 :             :  * a serialized string with newline ending, ready to be written to a stream.
    1266                 :             :  *
    1267                 :             :  * Returns: (transfer full) (nullable): the serialized packet.
    1268                 :             :  *
    1269                 :             :  * Since: 1.0
    1270                 :             :  */
    1271                 :             : char *
    1272                 :         319 : valent_packet_serialize (JsonNode *packet,
    1273                 :             :                          size_t   *length)
    1274                 :             : {
    1275                 :         319 :   JsonObject *root;
    1276                 :         638 :   g_autoptr (JsonGenerator) generator = NULL;
    1277                 :         319 :   GString *packet_str = NULL;
    1278                 :             : 
    1279         [ -  + ]:         319 :   g_return_val_if_fail (VALENT_IS_PACKET (packet), NULL);
    1280                 :             : 
    1281                 :             :   /* Timestamp the packet (UNIX Epoch ms)
    1282                 :             :    */
    1283                 :         319 :   root = json_node_get_object (packet);
    1284                 :         319 :   json_object_set_int_member (root, "id", valent_timestamp_ms ());
    1285                 :             : 
    1286                 :             :   /* Serialize the packet and append a newline character
    1287                 :             :    */
    1288                 :         319 :   generator = json_generator_new ();
    1289                 :         319 :   json_generator_set_root (generator, packet);
    1290                 :             : 
    1291                 :         319 :   packet_str = g_string_new ("");
    1292                 :         319 :   json_generator_to_gstring (generator, packet_str);
    1293         [ +  - ]:         319 :   g_string_append_c (packet_str, '\n');
    1294                 :             : 
    1295         [ +  + ]:         319 :   if (length != NULL)
    1296                 :         313 :     *length = packet_str->len;
    1297                 :             : 
    1298                 :         319 :   return g_string_free (packet_str, FALSE);
    1299                 :             : }
    1300                 :             : 
    1301                 :             : /**
    1302                 :             :  * valent_packet_deserialize:
    1303                 :             :  * @json: a complete KDE Connect packet
    1304                 :             :  * @error: (nullable): a `GError`
    1305                 :             :  *
    1306                 :             :  * Convenience function that deserializes a KDE Connect packet from a string
    1307                 :             :  * with basic validation. If @str is empty, this function will return %NULL.
    1308                 :             :  *
    1309                 :             :  * If parsing or validation fails, @error will be set and %NULL returned.
    1310                 :             :  *
    1311                 :             :  * Returns: (transfer full) (nullable): a KDE Connect packet
    1312                 :             :  *
    1313                 :             :  * Since: 1.0
    1314                 :             :  */
    1315                 :             : JsonNode *
    1316                 :         309 : valent_packet_deserialize (const char  *json,
    1317                 :             :                            GError     **error)
    1318                 :             : {
    1319                 :         618 :   g_autoptr (JsonParser) parser = NULL;
    1320         [ +  - ]:         309 :   g_autoptr (JsonNode) packet = NULL;
    1321                 :             : 
    1322         [ -  + ]:         309 :   g_return_val_if_fail (json != NULL, NULL);
    1323   [ +  -  +  - ]:         309 :   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
    1324                 :             : 
    1325                 :         309 :   parser = json_parser_new_immutable ();
    1326                 :             : 
    1327         [ +  - ]:         309 :   if (!json_parser_load_from_data (parser, json, -1, error))
    1328                 :             :     return NULL;
    1329                 :             : 
    1330         [ +  - ]:         309 :   if ((packet = json_parser_steal_root (parser)) == NULL)
    1331                 :             :     return NULL;
    1332                 :             : 
    1333         [ -  + ]:         309 :   if (!valent_packet_validate (packet, error))
    1334                 :           0 :     return NULL;
    1335                 :             : 
    1336                 :             :   return g_steal_pointer (&packet);
    1337                 :             : }
    1338                 :             : 
        

Generated by: LCOV version 2.0-1