LCOV - code coverage report
Current view: top level - src/libvalent/device - valent-packet.c (source / functions) Coverage Total Hit
Test: Code Coverage Lines: 93.1 % 331 308
Test Date: 2024-02-11 22:42:06 Functions: 100.0 % 27 27
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 61.1 % 342 209

             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         [ +  + ]:          15 : 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                 :           9 : valent_packet_new (const char *type)
      31                 :             : {
      32                 :          18 :   g_autoptr (JsonBuilder) builder = NULL;
      33                 :             : 
      34         [ +  - ]:           9 :   g_return_val_if_fail (type != NULL, NULL);
      35                 :             : 
      36                 :           9 :   builder = json_builder_new ();
      37                 :             : 
      38                 :           9 :   json_builder_begin_object (builder);
      39                 :           9 :   json_builder_set_member_name (builder, "id");
      40                 :           9 :   json_builder_add_int_value (builder, 0);
      41                 :           9 :   json_builder_set_member_name (builder, "type");
      42                 :           9 :   json_builder_add_string_value (builder, type);
      43                 :           9 :   json_builder_set_member_name (builder, "body");
      44                 :           9 :   json_builder_end_object (json_builder_begin_object (builder));
      45                 :           9 :   json_builder_end_object (builder);
      46                 :             : 
      47                 :           9 :   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                 :         151 : valent_packet_init (JsonBuilder **builder,
      77                 :             :                     const char   *type)
      78                 :             : {
      79   [ +  -  +  - ]:         151 :   g_return_if_fail (builder != NULL && *builder == NULL);
      80   [ +  -  -  + ]:         151 :   g_return_if_fail (type != NULL && *type != '\0');
      81                 :             : 
      82                 :         151 :   *builder = json_builder_new ();
      83                 :         151 :   json_builder_begin_object (*builder);
      84                 :         151 :   json_builder_set_member_name (*builder, "id");
      85                 :         151 :   json_builder_add_int_value (*builder, 0);
      86                 :         151 :   json_builder_set_member_name (*builder, "type");
      87                 :         151 :   json_builder_add_string_value (*builder, type);
      88                 :         151 :   json_builder_set_member_name (*builder, "body");
      89                 :             : 
      90                 :         151 :   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                 :         151 : valent_packet_end (JsonBuilder **builder)
     109                 :             : {
     110                 :         151 :   JsonNode *ret = NULL;
     111                 :             : 
     112   [ +  -  +  -  :         151 :   g_return_val_if_fail (builder != NULL && JSON_IS_BUILDER (*builder), NULL);
          +  -  -  +  -  
                      - ]
     113                 :             : 
     114                 :             :   /* Finish the `body` object and the root object */
     115                 :         151 :   json_builder_end_object (*builder);
     116                 :         151 :   json_builder_end_object (*builder);
     117                 :             : 
     118                 :         151 :   ret = json_builder_get_root (*builder);
     119         [ +  - ]:         151 :   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                 :        3652 : valent_packet_get_type (JsonNode *packet)
     163                 :             : {
     164                 :        3652 :   JsonObject *root;
     165                 :        3652 :   JsonNode *node;
     166                 :             : 
     167         [ +  - ]:        3652 :   g_return_val_if_fail (JSON_NODE_HOLDS_OBJECT (packet), NULL);
     168                 :             : 
     169                 :        3652 :   root = json_node_get_object (packet);
     170                 :             : 
     171   [ +  -  +  - ]:        3652 :   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                 :        3652 :   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                 :         404 : valent_packet_get_body (JsonNode *packet)
     190                 :             : {
     191                 :         404 :   JsonObject *root;
     192                 :         404 :   JsonNode *node;
     193                 :             : 
     194         [ +  - ]:         404 :   g_return_val_if_fail (JSON_NODE_HOLDS_OBJECT (packet), NULL);
     195                 :             : 
     196                 :         404 :   root = json_node_get_object (packet);
     197                 :             : 
     198   [ +  -  +  - ]:         404 :   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                 :         404 :   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                 :          43 : valent_packet_has_payload (JsonNode *packet)
     222                 :             : {
     223                 :          43 :   JsonObject *root;
     224                 :          43 :   JsonNode *node;
     225                 :             : 
     226         [ +  - ]:          43 :   g_return_val_if_fail (VALENT_IS_PACKET (packet), FALSE);
     227                 :             : 
     228                 :          43 :   root = json_node_get_object (packet);
     229                 :             : 
     230   [ +  +  -  + ]:          81 :   if ((node = json_object_get_member (root, "payloadSize")) != NULL &&
     231                 :          38 :       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                 :          28 :       json_node_get_node_type (node) != JSON_NODE_OBJECT)
     236                 :          15 :     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                 :          30 : valent_packet_get_payload_full (JsonNode  *packet,
     260                 :             :                                 goffset   *size,
     261                 :             :                                 GError   **error)
     262                 :             : {
     263                 :          30 :   JsonObject *root;
     264                 :          30 :   JsonNode *node;
     265                 :             : 
     266         [ -  + ]:          30 :   if (!valent_packet_validate (packet, error))
     267                 :             :     return NULL;
     268                 :             : 
     269                 :          30 :   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   [ +  -  -  + ]:          60 :   if ((node = json_object_get_member (root, "payloadSize")) != NULL &&
     274                 :          30 :       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         [ +  - ]:          30 :   if (size != NULL)
     284         [ +  - ]:          30 :     *size = node ? json_node_get_int (node) : -1;
     285                 :             : 
     286   [ +  -  -  + ]:          60 :   if ((node = json_object_get_member (root, "payloadTransferInfo")) == NULL ||
     287                 :          30 :       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                 :          30 :   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                 :          30 : valent_packet_set_payload_info (JsonNode   *packet,
     364                 :             :                                 JsonObject *info)
     365                 :             : {
     366                 :          30 :   JsonObject *root;
     367                 :             : 
     368         [ +  - ]:          30 :   g_return_if_fail (VALENT_IS_PACKET (packet));
     369         [ -  + ]:          30 :   g_return_if_fail (info != NULL);
     370                 :             : 
     371                 :          30 :   root = json_node_get_object (packet);
     372                 :             : 
     373                 :          30 :   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                 :          43 : valent_packet_get_payload_size (JsonNode *packet)
     388                 :             : {
     389                 :          43 :   JsonObject *root;
     390                 :          43 :   JsonNode *node;
     391                 :             : 
     392         [ +  - ]:          43 :   g_return_val_if_fail (VALENT_IS_PACKET (packet), 0);
     393                 :             : 
     394                 :          43 :   root = json_node_get_object (packet);
     395                 :          43 :   node = json_object_get_member (root, "payloadSize");
     396                 :             : 
     397   [ +  -  -  + ]:          86 :   if ((node = json_object_get_member (root, "payloadSize")) != NULL &&
     398                 :          43 :       json_node_get_value_type (node) != G_TYPE_INT64)
     399                 :           0 :     g_return_val_if_reached (-1);
     400                 :             : 
     401                 :          43 :   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                 :          39 : valent_packet_set_payload_size (JsonNode *packet,
     415                 :             :                                 goffset   size)
     416                 :             : {
     417                 :          39 :   JsonObject *root;
     418                 :             : 
     419         [ +  - ]:          39 :   g_return_if_fail (VALENT_IS_PACKET (packet));
     420         [ -  + ]:          39 :   g_return_if_fail (size >= -1);
     421                 :             : 
     422                 :          39 :   root = json_node_get_object (packet);
     423                 :             : 
     424                 :          39 :   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                 :         125 : valent_packet_check_field (JsonNode   *packet,
     443                 :             :                            const char *field)
     444                 :             : {
     445                 :         125 :   JsonObject *root;
     446                 :         125 :   JsonObject *body;
     447                 :         125 :   JsonNode *node;
     448                 :             : 
     449         [ +  - ]:         125 :   g_return_val_if_fail (JSON_NODE_HOLDS_OBJECT (packet), FALSE);
     450   [ +  -  -  + ]:         125 :   g_return_val_if_fail (field != NULL && *field != '\0', FALSE);
     451                 :             : 
     452                 :         125 :   root = json_node_get_object (packet);
     453                 :             : 
     454   [ +  -  +  - ]:         125 :   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                 :         125 :   body = json_node_get_object (node);
     459                 :             : 
     460         [ +  + ]:         125 :   if G_UNLIKELY ((node = json_object_get_member (body, field)) == NULL)
     461                 :             :     return FALSE;
     462                 :             : 
     463         [ +  + ]:          46 :   if (json_node_get_value_type (node) == G_TYPE_BOOLEAN)
     464                 :          33 :     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                 :          57 : valent_packet_get_boolean (JsonNode   *packet,
     489                 :             :                            const char *field,
     490                 :             :                            gboolean   *value)
     491                 :             : {
     492                 :          57 :   JsonObject *root, *body;
     493                 :          57 :   JsonNode *node;
     494                 :             : 
     495         [ +  - ]:          57 :   g_return_val_if_fail (JSON_NODE_HOLDS_OBJECT (packet), FALSE);
     496   [ +  -  -  + ]:          57 :   g_return_val_if_fail (field != NULL && *field != '\0', FALSE);
     497                 :             : 
     498                 :          57 :   root = json_node_get_object (packet);
     499                 :             : 
     500   [ +  -  +  - ]:          57 :   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                 :          57 :   body = json_node_get_object (node);
     505                 :             : 
     506   [ +  +  +  - ]:          57 :   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         [ +  - ]:          41 :   if (value)
     511                 :          41 :     *value = json_node_get_double (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                 :         148 : valent_packet_get_int (JsonNode   *packet,
     577                 :             :                        const char *field,
     578                 :             :                        int64_t    *value)
     579                 :             : {
     580                 :         148 :   JsonObject *root, *body;
     581                 :         148 :   JsonNode *node;
     582                 :             : 
     583         [ +  - ]:         148 :   g_return_val_if_fail (JSON_NODE_HOLDS_OBJECT (packet), FALSE);
     584   [ +  -  -  + ]:         148 :   g_return_val_if_fail (field != NULL && *field != '\0', FALSE);
     585                 :             : 
     586                 :         148 :   root = json_node_get_object (packet);
     587                 :             : 
     588   [ +  -  +  - ]:         148 :   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                 :         148 :   body = json_node_get_object (node);
     593                 :             : 
     594   [ +  +  +  - ]:         148 :   if G_UNLIKELY ((node = json_object_get_member (body, field)) == NULL ||
     595                 :             :                  json_node_get_value_type (node) != G_TYPE_INT64)
     596                 :          61 :     return FALSE;
     597                 :             : 
     598         [ +  + ]:          87 :   if (value)
     599                 :          82 :     *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                 :         792 : valent_packet_get_string (JsonNode    *packet,
     621                 :             :                           const char  *field,
     622                 :             :                           const char **value)
     623                 :             : {
     624                 :         792 :   JsonObject *root, *body;
     625                 :         792 :   JsonNode *node;
     626                 :         792 :   const char *string;
     627                 :             : 
     628         [ +  - ]:         792 :   g_return_val_if_fail (JSON_NODE_HOLDS_OBJECT (packet), FALSE);
     629   [ +  -  -  + ]:         792 :   g_return_val_if_fail (field != NULL && *field != '\0', FALSE);
     630                 :             : 
     631                 :         792 :   root = json_node_get_object (packet);
     632                 :             : 
     633   [ +  -  +  - ]:         792 :   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                 :         792 :   body = json_node_get_object (node);
     638                 :             : 
     639   [ +  +  +  - ]:         792 :   if G_UNLIKELY ((node = json_object_get_member (body, field)) == NULL ||
     640                 :             :                  json_node_get_value_type (node) != G_TYPE_STRING)
     641                 :          51 :     return FALSE;
     642                 :             : 
     643                 :         741 :   string = json_node_get_string (node);
     644                 :             : 
     645         [ -  + ]:         741 :   if G_UNLIKELY (*string == '\0')
     646                 :             :     return FALSE;
     647                 :             : 
     648         [ +  + ]:         741 :   if (value)
     649                 :         738 :     *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                 :          18 : valent_packet_get_array (JsonNode    *packet,
     671                 :             :                          const char  *field,
     672                 :             :                          JsonArray  **value)
     673                 :             : {
     674                 :          18 :   JsonObject *root, *body;
     675                 :          18 :   JsonNode *node;
     676                 :             : 
     677         [ +  - ]:          18 :   g_return_val_if_fail (JSON_NODE_HOLDS_OBJECT (packet), FALSE);
     678   [ +  -  -  + ]:          18 :   g_return_val_if_fail (field != NULL && *field != '\0', FALSE);
     679                 :             : 
     680                 :          18 :   root = json_node_get_object (packet);
     681                 :             : 
     682   [ +  -  +  - ]:          18 :   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                 :          18 :   body = json_node_get_object (node);
     687                 :             : 
     688   [ +  +  +  - ]:          18 :   if G_UNLIKELY ((node = json_object_get_member (body, field)) == NULL ||
     689                 :             :                  json_node_get_node_type (node) != JSON_NODE_ARRAY)
     690                 :           6 :     return FALSE;
     691                 :             : 
     692         [ +  - ]:          12 :   if (value)
     693                 :          12 :     *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                 :          23 : valent_packet_get_object (JsonNode    *packet,
     715                 :             :                           const char  *field,
     716                 :             :                           JsonObject **value)
     717                 :             : {
     718                 :          23 :   JsonObject *root, *body;
     719                 :          23 :   JsonNode *node;
     720                 :             : 
     721         [ +  - ]:          23 :   g_return_val_if_fail (JSON_NODE_HOLDS_OBJECT (packet), FALSE);
     722   [ +  -  -  + ]:          23 :   g_return_val_if_fail (field != NULL && *field != '\0', FALSE);
     723                 :             : 
     724                 :          23 :   root = json_node_get_object (packet);
     725                 :             : 
     726   [ +  -  +  - ]:          23 :   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                 :          23 :   body = json_node_get_object (node);
     731                 :             : 
     732   [ +  -  +  - ]:          23 :   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         [ +  - ]:          23 :   if (value)
     737                 :          23 :     *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                 :         341 : valent_packet_dup_strv (JsonNode   *packet,
     759                 :             :                         const char *field)
     760                 :             : {
     761                 :         341 :   JsonObject *root, *body;
     762                 :         341 :   JsonNode *node;
     763                 :         341 :   JsonArray *array;
     764                 :         682 :   g_auto (GStrv) strv = NULL;
     765                 :         341 :   unsigned int n_strings;
     766                 :             : 
     767         [ +  - ]:         341 :   g_return_val_if_fail (JSON_NODE_HOLDS_OBJECT (packet), NULL);
     768   [ +  -  -  + ]:         341 :   g_return_val_if_fail (field != NULL && *field != '\0', NULL);
     769                 :             : 
     770                 :             : #ifndef __clang_analyzer__
     771                 :         341 :   root = json_node_get_object (packet);
     772                 :             : 
     773   [ +  -  +  - ]:         341 :   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                 :         341 :   body = json_node_get_object (node);
     778                 :             : 
     779   [ +  -  +  - ]:         341 :   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                 :         341 :   array = json_node_get_array (node);
     784                 :         341 :   n_strings = json_array_get_length (array);
     785         [ -  + ]:         341 :   strv = g_new0 (char *, n_strings + 1);
     786                 :             : 
     787         [ +  + ]:        1027 :   for (unsigned int i = 0; i < n_strings; i++)
     788                 :             :     {
     789                 :         686 :       JsonNode *element = json_array_get_element (array, i);
     790                 :             : 
     791         [ +  - ]:         686 :       if G_UNLIKELY (json_node_get_value_type (element) != G_TYPE_STRING)
     792                 :             :         return NULL;
     793                 :             : 
     794                 :         686 :       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                 :        7387 : valent_packet_validate (JsonNode  *packet,
     814                 :             :                         GError   **error)
     815                 :             : {
     816                 :        7387 :   JsonObject *root;
     817                 :        7387 :   JsonNode *node;
     818                 :             : 
     819         [ +  + ]:        7387 :   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         [ +  + ]:        7385 :   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                 :        7383 :   root = json_node_get_object (packet);
     838                 :             : 
     839                 :             :   /* TODO: kdeconnect-kde stringifies this in identity packets
     840                 :             :    *       https://invent.kde.org/network/kdeconnect-kde/-/merge_requests/380 */
     841   [ +  +  +  +  :        7383 :   if G_UNLIKELY ((node = json_object_get_member (root, "id")) == NULL ||
                   +  + ]
     842                 :             :                  (json_node_get_value_type (node) != G_TYPE_INT64 &&
     843                 :             :                   json_node_get_value_type (node) != G_TYPE_STRING))
     844                 :             :     {
     845         [ +  + ]:           3 :       g_set_error_literal (error,
     846                 :             :                            VALENT_PACKET_ERROR,
     847                 :             :                            node == NULL
     848                 :             :                              ? VALENT_PACKET_ERROR_MISSING_FIELD
     849                 :             :                              : VALENT_PACKET_ERROR_INVALID_FIELD,
     850                 :             :                            "expected \"id\" field holding an integer or string");
     851                 :           2 :       return FALSE;
     852                 :             :     }
     853                 :             : 
     854   [ +  +  +  + ]:        7381 :   if G_UNLIKELY ((node = json_object_get_member (root, "type")) == NULL ||
     855                 :             :                  json_node_get_value_type (node) != G_TYPE_STRING)
     856                 :             :     {
     857         [ +  + ]:           3 :       g_set_error_literal (error,
     858                 :             :                            VALENT_PACKET_ERROR,
     859                 :             :                            node == NULL
     860                 :             :                              ? VALENT_PACKET_ERROR_MISSING_FIELD
     861                 :             :                              : VALENT_PACKET_ERROR_INVALID_FIELD,
     862                 :             :                            "expected \"type\" field holding a string");
     863                 :           2 :       return FALSE;
     864                 :             :     }
     865                 :             : 
     866   [ +  +  +  + ]:        7379 :   if G_UNLIKELY ((node = json_object_get_member (root, "body")) == NULL ||
     867                 :             :                  json_node_get_node_type (node) != JSON_NODE_OBJECT)
     868                 :             :     {
     869         [ +  + ]:           3 :       g_set_error_literal (error,
     870                 :             :                            VALENT_PACKET_ERROR,
     871                 :             :                            node == NULL
     872                 :             :                              ? VALENT_PACKET_ERROR_MISSING_FIELD
     873                 :             :                              : VALENT_PACKET_ERROR_INVALID_FIELD,
     874                 :             :                            "expected \"body\" field holding an object");
     875                 :           2 :       return FALSE;
     876                 :             :     }
     877                 :             : 
     878                 :             :   /* These two are optional, but have defined value types */
     879   [ +  +  +  + ]:        7377 :   if G_UNLIKELY ((node = json_object_get_member (root, "payloadSize")) != NULL &&
     880                 :             :                  json_node_get_value_type (node) != G_TYPE_INT64)
     881                 :             :     {
     882                 :           1 :       g_set_error_literal (error,
     883                 :             :                            VALENT_PACKET_ERROR,
     884                 :             :                            VALENT_PACKET_ERROR_INVALID_FIELD,
     885                 :             :                            "expected \"payloadSize\" field to hold an integer");
     886                 :           1 :       return FALSE;
     887                 :             :     }
     888                 :             : 
     889   [ +  +  +  + ]:        7376 :   if G_UNLIKELY ((node = json_object_get_member (root, "payloadTransferInfo")) != NULL &&
     890                 :             :                  json_node_get_node_type (node) != JSON_NODE_OBJECT)
     891                 :             :     {
     892                 :           1 :       g_set_error_literal (error,
     893                 :             :                            VALENT_PACKET_ERROR,
     894                 :             :                            VALENT_PACKET_ERROR_INVALID_FIELD,
     895                 :             :                            "expected \"payloadTransferInfo\" field to hold an object");
     896                 :           1 :       return FALSE;
     897                 :             :     }
     898                 :             : 
     899                 :             :   return TRUE;
     900                 :             : }
     901                 :             : 
     902                 :             : /**
     903                 :             :  * valent_packet_from_stream:
     904                 :             :  * @stream: a `GInputStream`
     905                 :             :  * @max_len: the maximum number bytes to read, or `-1` for no limit
     906                 :             :  * @cancellable: (nullable): a `GCancellable`
     907                 :             :  * @error: (nullable): a `GError`
     908                 :             :  *
     909                 :             :  * Read a KDE Connect packet from an input stream.
     910                 :             :  *
     911                 :             :  * If reading fails or the packet does not conform to the minimum structure of
     912                 :             :  * a KDE Connect packet, %NULL will be returned with @error set.
     913                 :             :  *
     914                 :             :  * If @max_len is greater than `-1`, then at most @max_len bytes will be read.
     915                 :             :  * If @max_len bytes are read without encountering a line-feed character, %NULL
     916                 :             :  * will be returned with @error set to %G_IO_ERROR_MESSAGE_TOO_LARGE.
     917                 :             :  *
     918                 :             :  * Returns: (transfer full): a KDE Connect packet, or %NULL with @error set.
     919                 :             :  *
     920                 :             :  * Since: 1.0
     921                 :             :  */
     922                 :             : JsonNode *
     923                 :          14 : valent_packet_from_stream (GInputStream  *stream,
     924                 :             :                            gssize         max_len,
     925                 :             :                            GCancellable  *cancellable,
     926                 :             :                            GError       **error)
     927                 :             : {
     928                 :          28 :   g_autoptr (JsonParser) parser = NULL;
     929         [ +  + ]:          14 :   g_autoptr (JsonNode) packet = NULL;
     930         [ -  + ]:          14 :   g_autofree char *line = NULL;
     931                 :          14 :   gssize count = 0;
     932                 :          14 :   gssize size = 4096;
     933                 :             : 
     934   [ +  -  +  -  :          14 :   g_return_val_if_fail (G_IS_INPUT_STREAM (stream), NULL);
             +  -  -  + ]
     935   [ +  +  +  -  :          14 :   g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
             -  +  -  - ]
     936   [ +  -  -  + ]:          14 :   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
     937                 :             : 
     938                 :             : #ifndef __clang_analyzer__
     939         [ +  + ]:          14 :   if (max_len < 0)
     940                 :          10 :     max_len = G_MAXSSIZE;
     941                 :             : 
     942                 :          14 :   line = g_malloc0 (size);
     943                 :             : 
     944                 :        6498 :   while (TRUE)
     945                 :             :     {
     946                 :        6498 :       gssize read = 0;
     947                 :             : 
     948         [ +  + ]:        6498 :       if G_UNLIKELY (count == max_len)
     949                 :             :         {
     950                 :           1 :           g_set_error (error,
     951                 :             :                        G_IO_ERROR,
     952                 :             :                        G_IO_ERROR_MESSAGE_TOO_LARGE,
     953                 :             :                        "Packet too large");
     954                 :           1 :           return NULL;
     955                 :             :         }
     956                 :             : 
     957         [ +  + ]:        6497 :       if G_UNLIKELY (count == size)
     958                 :             :         {
     959                 :           1 :           size = MIN (size * 2, max_len);
     960                 :           1 :           line = g_realloc (line, size);
     961                 :             :         }
     962                 :             : 
     963                 :       12994 :       read = g_input_stream_read (stream,
     964                 :        6497 :                                   line + count,
     965                 :             :                                   1,
     966                 :             :                                   cancellable,
     967                 :             :                                   error);
     968                 :             : 
     969         [ +  + ]:        6497 :       if (read > 0)
     970                 :        6493 :         count += read;
     971         [ +  + ]:           4 :       else if (read == 0)
     972                 :             :         break;
     973                 :             :       else
     974                 :             :         return NULL;
     975                 :             : 
     976   [ +  +  +  + ]:        6493 :       if G_UNLIKELY (line[count - 1] == '\n')
     977                 :             :         break;
     978                 :             :     }
     979                 :             : 
     980                 :          12 :   parser = json_parser_new_immutable ();
     981                 :             : 
     982         [ +  + ]:          12 :   if (!json_parser_load_from_data (parser, line, count, error))
     983                 :             :     return NULL;
     984                 :             : 
     985                 :          11 :   packet = json_parser_steal_root (parser);
     986                 :             : 
     987         [ +  + ]:          11 :   if (!valent_packet_validate (packet, error))
     988                 :           2 :     return NULL;
     989                 :             : #endif /* __clang_analyzer__ */
     990                 :             : 
     991                 :             :   return g_steal_pointer (&packet);
     992                 :             : }
     993                 :             : 
     994                 :             : /**
     995                 :             :  * valent_packet_to_stream:
     996                 :             :  * @stream: a `GOutputStream`
     997                 :             :  * @packet: a KDE Connect packet
     998                 :             :  * @cancellable: (nullable): a `GCancellable`
     999                 :             :  * @error: (nullable): a `GError`
    1000                 :             :  *
    1001                 :             :  * A convenience function for writing a packet to a connection.
    1002                 :             :  *
    1003                 :             :  * Returns: %TRUE if successful, or %FALSE with @error set
    1004                 :             :  *
    1005                 :             :  * Since: 1.0
    1006                 :             :  */
    1007                 :             : gboolean
    1008                 :        3671 : valent_packet_to_stream (GOutputStream  *stream,
    1009                 :             :                          JsonNode       *packet,
    1010                 :             :                          GCancellable   *cancellable,
    1011                 :             :                          GError        **error)
    1012                 :             : {
    1013                 :        7342 :   g_autoptr (JsonGenerator) generator = NULL;
    1014                 :        3671 :   JsonObject *root;
    1015         [ +  - ]:        3671 :   g_autofree char *packet_str = NULL;
    1016                 :        3671 :   size_t packet_len;
    1017                 :        3671 :   size_t n_written;
    1018                 :             : 
    1019   [ +  -  +  -  :        3671 :   g_return_val_if_fail (G_IS_OUTPUT_STREAM (stream), FALSE);
             +  -  -  + ]
    1020   [ +  +  +  -  :        3671 :   g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
             -  +  -  - ]
    1021   [ +  -  -  + ]:        3671 :   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
    1022                 :             : 
    1023         [ -  + ]:        3671 :   if (!valent_packet_validate (packet, error))
    1024                 :             :     return FALSE;
    1025                 :             : 
    1026                 :             :   /* Timestamp the packet (UNIX Epoch ms) */
    1027                 :        3671 :   root = json_node_get_object (packet);
    1028                 :        3671 :   json_object_set_int_member (root, "id", valent_timestamp_ms ());
    1029                 :             : 
    1030                 :             :   /* Serialize the packet and replace the trailing NULL with an LF */
    1031                 :        3671 :   generator = json_generator_new ();
    1032                 :        3671 :   json_generator_set_root (generator, packet);
    1033                 :        3671 :   packet_str = json_generator_to_data (generator, &packet_len);
    1034                 :        3671 :   packet_str[packet_len++] = '\n';
    1035                 :             : 
    1036         [ +  + ]:        3671 :   if (!g_output_stream_write_all (stream,
    1037                 :             :                                   packet_str,
    1038                 :             :                                   packet_len,
    1039                 :             :                                   &n_written,
    1040                 :             :                                   cancellable,
    1041                 :             :                                   error))
    1042                 :             :     return FALSE;
    1043                 :             : 
    1044         [ -  + ]:        3670 :   if (n_written != packet_len)
    1045                 :             :     {
    1046                 :           0 :       g_set_error (error,
    1047                 :             :                    G_IO_ERROR,
    1048                 :             :                    G_IO_ERROR_CONNECTION_CLOSED,
    1049                 :             :                    "Channel is closed");
    1050                 :           0 :       return FALSE;
    1051                 :             :     }
    1052                 :             : 
    1053                 :             :   return TRUE;
    1054                 :             : }
    1055                 :             : 
    1056                 :             : /**
    1057                 :             :  * valent_packet_serialize:
    1058                 :             :  * @packet: a complete KDE Connect packet
    1059                 :             :  *
    1060                 :             :  * Convenience function that updates the timestamp of a packet before returning
    1061                 :             :  * a serialized string with newline ending, ready to be written to a stream.
    1062                 :             :  *
    1063                 :             :  * Returns: (transfer full) (nullable): the serialized packet.
    1064                 :             :  *
    1065                 :             :  * Since: 1.0
    1066                 :             :  */
    1067                 :             : char *
    1068                 :          15 : valent_packet_serialize (JsonNode *packet)
    1069                 :             : {
    1070                 :          30 :   g_autoptr (JsonGenerator) generator = NULL;
    1071                 :          15 :   JsonObject *root;
    1072         [ +  - ]:          15 :   g_autofree char *packet_str = NULL;
    1073                 :             : 
    1074         [ +  - ]:          15 :   g_return_val_if_fail (VALENT_IS_PACKET (packet), NULL);
    1075                 :             : 
    1076                 :             :   /* Timestamp the packet (UNIX Epoch ms) */
    1077                 :          15 :   root = json_node_get_object (packet);
    1078                 :          15 :   json_object_set_int_member (root, "id", valent_timestamp_ms ());
    1079                 :             : 
    1080                 :             :   /* Stringify the packet and return a newline-terminated string */
    1081                 :          15 :   generator = json_generator_new ();
    1082                 :          15 :   json_generator_set_root (generator, packet);
    1083                 :          15 :   packet_str = json_generator_to_data (generator, NULL);
    1084                 :             : 
    1085                 :          15 :   return g_strconcat (packet_str, "\n", NULL);
    1086                 :             : }
    1087                 :             : 
    1088                 :             : /**
    1089                 :             :  * valent_packet_deserialize:
    1090                 :             :  * @json: a complete KDE Connect packet
    1091                 :             :  * @error: (nullable): a `GError`
    1092                 :             :  *
    1093                 :             :  * Convenience function that deserializes a KDE Connect packet from a string
    1094                 :             :  * with basic validation. If @str is empty, this function will return %NULL.
    1095                 :             :  *
    1096                 :             :  * If parsing or validation fails, @error will be set and %NULL returned.
    1097                 :             :  *
    1098                 :             :  * Returns: (transfer full) (nullable): a KDE Connect packet
    1099                 :             :  *
    1100                 :             :  * Since: 1.0
    1101                 :             :  */
    1102                 :             : JsonNode *
    1103                 :        3665 : valent_packet_deserialize (const char  *json,
    1104                 :             :                            GError     **error)
    1105                 :             : {
    1106                 :        7330 :   g_autoptr (JsonParser) parser = NULL;
    1107         [ +  - ]:        3665 :   g_autoptr (JsonNode) packet = NULL;
    1108                 :             : 
    1109         [ +  - ]:        3665 :   g_return_val_if_fail (json != NULL, NULL);
    1110   [ +  -  -  + ]:        3665 :   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
    1111                 :             : 
    1112                 :        3665 :   parser = json_parser_new_immutable ();
    1113                 :             : 
    1114         [ -  + ]:        3665 :   if (!json_parser_load_from_data (parser, json, -1, error))
    1115                 :             :     return NULL;
    1116                 :             : 
    1117         [ -  + ]:        3665 :   if ((packet = json_parser_steal_root (parser)) == NULL)
    1118                 :             :     return NULL;
    1119                 :             : 
    1120         [ -  + ]:        3665 :   if (!valent_packet_validate (packet, error))
    1121                 :           0 :     return NULL;
    1122                 :             : 
    1123                 :             :   return g_steal_pointer (&packet);
    1124                 :             : }
    1125                 :             : 
        

Generated by: LCOV version 2.0-1