LCOV - code coverage report
Current view: top level - src/plugins/base64 - base64.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 86 98 87.8 %
Date: 2022-05-21 16:19:22 Functions: 9 10 90.0 %

          Line data    Source code
       1             : /**
       2             :  * @file
       3             :  *
       4             :  * @brief filter plugin for the Base64 encoding
       5             :  *
       6             :  * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
       7             :  *
       8             :  */
       9             : 
      10             : #include "base64.h"
      11             : #include <kdb.h>
      12             : #include <kdberrors.h>
      13             : #include <stdbool.h>
      14             : #include <string.h>
      15             : 
      16             : /**
      17             :  * @brief Unescape a key value starting with two `ELEKTRA_PLUGIN_BASE64_ESCAPE` characters (`@@`).
      18             :  *
      19             :  * @pre The type of the key value must be string.
      20             :  *
      21             :  * @param key This parameter contains the key value this function escapes.
      22             :  * @param parent The function stores information about errors in this parameter.
      23             :  *
      24             :  * @retval -1 if the function was unable to unescape the value of `key`
      25             :  * @retval 0 if the given key was not modified
      26             :  * @retval 1 if the function successfully unescaped `key`
      27             :  */
      28         445 : static int unescape (Key * key, Key * parent)
      29             : {
      30         445 :         const char * strVal = keyString (key);
      31         445 :         const char escapedPrefix[] = ELEKTRA_PLUGIN_BASE64_ESCAPE ELEKTRA_PLUGIN_BASE64_ESCAPE;
      32             : 
      33         445 :         if (strlen (strVal) < 2 || strncmp (strVal, escapedPrefix, 2) != 0) return 0;
      34             : 
      35             :         // Discard the first escape character
      36           3 :         char * unescaped = elektraStrDup (&strVal[1]);
      37           3 :         if (!unescaped)
      38             :         {
      39           0 :                 ELEKTRA_SET_OUT_OF_MEMORY_ERROR (parent);
      40           0 :                 return -1;
      41             :         }
      42           3 :         keySetString (key, unescaped);
      43           3 :         elektraFree (unescaped);
      44           3 :         return 1;
      45             : }
      46             : 
      47             : /**
      48             :  * @brief Check if the given key should be decoded by the plugin.
      49             :  *
      50             :  * @pre The type of the key value must be string.
      51             :  *
      52             :  * @param key This parameter specifies the key for which this function decides if it should be encoded or not.
      53             :  * @param metaMode This parameter specifies if the plugin uses meta mode or not.
      54             :  *
      55             :  * @retval true if the plugin should decode the given key
      56             :  * @retval false otherwise
      57             :  */
      58         832 : static bool shouldDecode (Key * key, bool metaMode)
      59             : {
      60         854 :         if (metaMode) return keyGetMeta (key, "type") && strcmp (keyValue (keyGetMeta (key, "type")), "binary") == 0;
      61             : 
      62         461 :         const char * strVal = keyString (key);
      63         922 :         return strlen (strVal) >= ELEKTRA_PLUGIN_BASE64_PREFIX_LENGTH &&
      64         104 :                strncmp (strVal, ELEKTRA_PLUGIN_BASE64_PREFIX, ELEKTRA_PLUGIN_BASE64_PREFIX_LENGTH) == 0;
      65             : }
      66             : 
      67             : /**
      68             :  * @brief Decode a base64 encoded key value and save the result as binary data in the key.
      69             :  *
      70             :  * The conversion only happens if the value of the key has type `string` and
      71             :  *
      72             :  *   1. in escaping mode: the key value starts with `ELEKTRA_PLUGIN_BASE64_PREFIX` (`"@BASE64"`),
      73             :  *   2. in meta mode: the key contains the metakey `type` with the value `binary`
      74             :  *
      75             :  * . If the key value starts with two prefix characters (`@@`) in **escaping mode**, then the function unescapes the value by removing one
      76             :  * of the prefix characters.
      77             :  *
      78             :  * @param key This parameter specifies the key that this function decodes.
      79             :  * @param parent The function stores information about errors/warnings in this parameter.
      80             :  * @param metaMode This parameter specifies if the plugin uses meta mode or not.
      81             :  *
      82             :  * @retval -1 if the function was unable to convert or unescape the value of `key`
      83             :  * @retval 0 if the given key was not modified
      84             :  * @retval 1 if the function successfully modified `key`
      85             :  */
      86         918 : static int decode (Key * key, Key * parent, bool metaMode)
      87             : {
      88         918 :         if (!keyIsString (key)) return 0;
      89             : 
      90         832 :         if (!shouldDecode (key, metaMode))
      91             :         {
      92         809 :                 if (metaMode) return 0;
      93         445 :                 return unescape (key, parent);
      94             :         }
      95             : 
      96          23 :         ELEKTRA_LOG_DEBUG ("Decode binary value");
      97             : 
      98          23 :         kdb_octet_t * buffer;
      99          23 :         size_t bufferLen;
     100          23 :         const char * strVal = keyString (key);
     101          39 :         int result = base64Decode (strVal + (metaMode ? 0 : ELEKTRA_PLUGIN_BASE64_PREFIX_LENGTH), &buffer, &bufferLen);
     102          23 :         if (result == 1)
     103             :         {
     104             :                 // Success
     105          20 :                 keySetBinary (key, buffer, bufferLen);
     106             :         }
     107           3 :         else if (result == -1)
     108             :         {
     109             :                 // Decoding error
     110           3 :                 ELEKTRA_ADD_VALIDATION_SYNTACTIC_WARNINGF (parent, "Key %s was not Base64 encoded: %s", keyName (key), strVal);
     111             :         }
     112           0 :         else if (result == -2)
     113             :         {
     114             :                 // Memory error
     115           0 :                 ELEKTRA_SET_OUT_OF_MEMORY_ERROR (parent);
     116           0 :                 return -1;
     117             :         }
     118             : 
     119          23 :         elektraFree (buffer);
     120          23 :         return 1;
     121             : }
     122             : 
     123             : /**
     124             :  * @brief Encode a binary key value using base64 encoding and save the result as textual data in the key.
     125             :  *
     126             :  * @param key This parameter specifies the key that this function encodes.
     127             :  * @param parent The function stores information about errors in this parameter.
     128             :  * @param metaMode This parameter specifies if the plugin uses meta mode or not.
     129             :  *
     130             :  * @retval -1 if the function was unable to convert the value of `key`
     131             :  * @retval 0 if no conversion has taken place
     132             :  * @retval 1 if the function successfully converted the value of `key`
     133             :  */
     134         395 : static int encode (Key * key, Key * parent, bool metaMode)
     135             : {
     136         395 :         if (!keyIsBinary (key) || (keyGetValueSize (key) == 0 && metaMode)) return 0;
     137             : 
     138          11 :         char * base64 = base64Encode (keyValue (key), (size_t) keyGetValueSize (key));
     139          11 :         if (!base64)
     140             :         {
     141           0 :                 ELEKTRA_SET_OUT_OF_MEMORY_ERROR (parent);
     142           0 :                 return -1;
     143             :         }
     144             : 
     145          11 :         if (metaMode)
     146             :         {
     147           1 :                 keySetString (key, base64);
     148             :         }
     149             :         else
     150             :         {
     151          10 :                 const size_t newValLen = strlen (base64) + ELEKTRA_PLUGIN_BASE64_PREFIX_LENGTH + 1;
     152          10 :                 char * newVal = elektraMalloc (newValLen);
     153          10 :                 if (!newVal)
     154             :                 {
     155           0 :                         ELEKTRA_SET_OUT_OF_MEMORY_ERROR (parent);
     156           0 :                         elektraFree (base64);
     157           0 :                         return -1;
     158             :                 }
     159          10 :                 snprintf (newVal, newValLen, "%s%s", ELEKTRA_PLUGIN_BASE64_PREFIX, base64); //! OCLint (constant conditional operator)
     160          10 :                 keySetString (key, newVal);
     161          10 :                 elektraFree (newVal);
     162             :         }
     163             : 
     164          11 :         elektraFree (base64);
     165             : 
     166          11 :         return 1;
     167             : }
     168             : 
     169             : /**
     170             :  * @brief Escape the prefix character `ELEKTRA_PLUGIN_BASE64_PREFIX` (`@`) in a key value by prefixing it with another `@`.
     171             :  *
     172             :  * This function only inserts another prefix character if the type of `key` is string.
     173             :  *
     174             :  * @param key This parameter specifies the key value that this function escapes.
     175             :  * @param parent The function stores information about errors in this parameter.
     176             :  *
     177             :  * @retval -1 if the function was unable to escape the key value
     178             :  * @retval 0 if the function did not change the key value
     179             :  * @retval 1 if the function successfully escaped the value of `key`
     180             :  */
     181         201 : static int escape (Key * key, Key * parent)
     182             : {
     183         201 :         if (keyIsString (key) == 0) return 0;
     184             : 
     185             :         // escape the prefix character
     186         191 :         const char * strVal = keyString (key);
     187         191 :         const size_t strValLen = strlen (strVal);
     188         191 :         if (strValLen <= 0 || strVal[0] != ELEKTRA_PLUGIN_BASE64_ESCAPE_CHAR) return 0;
     189             : 
     190             :         // + 1 for the additional escape character
     191             :         // + 1 for the NULL terminator
     192           3 :         char * escapedVal = elektraMalloc (strValLen + 2);
     193           3 :         if (!escapedVal)
     194             :         {
     195           0 :                 ELEKTRA_SET_OUT_OF_MEMORY_ERROR (parent);
     196           0 :                 return -1;
     197             :         }
     198             : 
     199             :         // add the escape character in front of the original value
     200           3 :         escapedVal[0] = ELEKTRA_PLUGIN_BASE64_ESCAPE_CHAR;
     201           3 :         strncpy (&escapedVal[1], strVal, strValLen + 1); //! OCLint (constant conditional operator)
     202           3 :         keySetString (key, escapedVal);
     203           3 :         elektraFree (escapedVal);
     204           3 :         return 1;
     205             : }
     206             : 
     207             : /**
     208             :  * @brief Check if the plugin should use meta mode for the base64 conversion.
     209             :  *
     210             :  * @param handle This parameter stores the configuration of the plugin.
     211             :  *
     212             :  * @retval true if the plugin should use meta mode
     213             :  * @retval false if the plugin should use escaping mode
     214             :  */
     215         445 : static bool useMetaMode (Plugin * handle)
     216             : {
     217         445 :         KeySet * config = elektraPluginGetConfig (handle);
     218         445 :         Key * metaMode = ksLookupByName (config, "/binary/meta", 0);
     219             : 
     220         445 :         ELEKTRA_LOG ("Using %s mode", metaMode ? "meta" : "escaping");
     221         445 :         return metaMode ? true : false;
     222             : }
     223             : 
     224             : /**
     225             :  * @brief Establish the Elektra plugin contract and decode all Base64 encoded values back to their original binary form.
     226             :  *
     227             :  * @param handle This parameter stores the configuration of the plugin.
     228             :  * @param keySet This parameter specifies the key set that this function updates.
     229             :  * @param parent The function stores information about errors/warnings in this parameter.
     230             :  *
     231             :  * @retval 1 if any keys were updated
     232             :  * @retval 0 if `keyset` was not modified
     233             :  * @retval -1 on failure
     234             :  */
     235         832 : int PLUGIN_FUNCTION (get) (Plugin * handle, KeySet * keySet, Key * parentKey)
     236             : {
     237             :         // Publish module configuration to Elektra (establish the contract)
     238         832 :         if (!strcmp (keyName (parentKey), "system:/elektra/modules/" ELEKTRA_PLUGIN_NAME))
     239             :         {
     240         524 :                 KeySet * moduleConfig = ksNew (30,
     241             : #include "contract.h"
     242             :                                                KS_END);
     243         524 :                 ksAppend (keySet, moduleConfig);
     244         524 :                 ksDel (moduleConfig);
     245         524 :                 return 1;
     246             :         }
     247             : 
     248         308 :         bool metaMode = useMetaMode (handle);
     249             : 
     250             :         // base64 decoding
     251         308 :         Key * key;
     252         308 :         ksRewind (keySet);
     253         308 :         int status = 0;
     254        1226 :         while (status >= 0 && (key = ksNext (keySet)))
     255             :         {
     256         918 :                 status |= decode (key, parentKey, metaMode);
     257             :         }
     258             :         return status;
     259             : }
     260             : 
     261             : /**
     262             :  * @brief Encode all binary values using the Base64 encoding scheme.
     263             :  *
     264             :  * @param handle This parameter stores the configuration of the plugin.
     265             :  * @param keySet This parameter specifies the key set that this function updates.
     266             :  * @param parent The function stores information about errors in this parameter.
     267             :  *
     268             :  * @retval 1 if any keys were updated
     269             :  * @retval 0 if `keyset` was not modified
     270             :  * @retval -1 on failure
     271             :  */
     272         137 : int PLUGIN_FUNCTION (set) (Plugin * handle, KeySet * keySet, Key * parentKey)
     273             : {
     274         137 :         Key * key;
     275             : 
     276         137 :         ksRewind (keySet);
     277             : 
     278         137 :         bool metaMode = useMetaMode (handle);
     279             : 
     280         137 :         int status = 0;
     281         532 :         while (status >= 0 && (key = ksNext (keySet)))
     282             :         {
     283         395 :                 if (!metaMode)
     284             :                 {
     285         201 :                         status |= escape (key, parentKey);
     286         201 :                         if (status < 0) break;
     287             :                 }
     288         395 :                 status |= encode (key, parentKey, metaMode);
     289             :         }
     290         137 :         return status;
     291             : }
     292             : 
     293        2140 : Plugin * ELEKTRA_PLUGIN_EXPORT
     294             : {
     295        2140 :         return elektraPluginExport (ELEKTRA_PLUGIN_NAME, ELEKTRA_PLUGIN_GET, &PLUGIN_FUNCTION (get), ELEKTRA_PLUGIN_SET,
     296             :                                     &PLUGIN_FUNCTION (set), ELEKTRA_PLUGIN_END);
     297             : }

Generated by: LCOV version 1.13