LCOV - code coverage report
Current view: top level - extensions - extensions.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 27.2 % 151 41
Test Date: 2026-01-18 12:54:31 Functions: 41.7 % 12 5

            Line data    Source code
       1              : /*
       2              :    This file is part of TALER
       3              :    Copyright (C) 2021-2022 Taler Systems SA
       4              : 
       5              :    TALER is free software; you can redistribute it and/or modify it under the
       6              :    terms of the GNU General Public License as published by the Free Software
       7              :    Foundation; either version 3, or (at your option) any later version.
       8              : 
       9              :    TALER is distributed in the hope that it will be useful, but WITHOUT ANY
      10              :    WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      11              :    A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
      12              : 
      13              :    You should have received a copy of the GNU General Public License along with
      14              :    TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
      15              :  */
      16              : /**
      17              :  * @file extensions.c
      18              :  * @brief Utility functions for extensions
      19              :  * @author Özgür Kesim
      20              :  */
      21              : #include "taler/platform.h"
      22              : #include "taler/taler_extensions_policy.h"
      23              : #include "taler/taler_util.h"
      24              : #include "taler/taler_signatures.h"
      25              : #include "taler/taler_extensions.h"
      26              : #include "stdint.h"
      27              : 
      28              : /* head of the list of all registered extensions */
      29              : static struct TALER_Extensions TE_extensions = {
      30              :   .next = NULL,
      31              :   .extension = NULL,
      32              : };
      33              : 
      34              : const struct TALER_Extensions *
      35          114 : TALER_extensions_get_head ()
      36              : {
      37          114 :   return &TE_extensions;
      38              : }
      39              : 
      40              : 
      41              : static enum GNUNET_GenericReturnValue
      42           50 : add_extension (
      43              :   const struct TALER_Extension *extension)
      44              : {
      45              :   /* Sanity checks */
      46           50 :   if ((NULL == extension) ||
      47           50 :       (NULL == extension->name) ||
      48           50 :       (NULL == extension->version) ||
      49           50 :       (NULL == extension->disable) ||
      50           50 :       (NULL == extension->load_config) ||
      51           50 :       (NULL == extension->manifest))
      52              :   {
      53            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
      54              :                 "invalid extension\n");
      55            0 :     return GNUNET_SYSERR;
      56              :   }
      57              : 
      58           50 :   if (NULL == TE_extensions.extension) /* first extension ?*/
      59           50 :     TE_extensions.extension = extension;
      60              :   else
      61              :   {
      62              :     struct TALER_Extensions *iter;
      63              :     struct TALER_Extensions *last;
      64              : 
      65              :     /* Check for collisions */
      66            0 :     for (iter = &TE_extensions;
      67            0 :          NULL != iter && NULL != iter->extension;
      68            0 :          iter = iter->next)
      69              :     {
      70            0 :       const struct TALER_Extension *ext = iter->extension;
      71            0 :       last = iter;
      72            0 :       if (extension->type == ext->type ||
      73            0 :           0 == strcasecmp (extension->name,
      74            0 :                            ext->name))
      75              :       {
      76            0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
      77              :                     "extension collision for `%s'\n",
      78              :                     extension->name);
      79            0 :         return GNUNET_NO;
      80              :       }
      81              :     }
      82              : 
      83              :     /* No collisions found, so add this extension to the list */
      84              :     {
      85            0 :       struct TALER_Extensions *extn = GNUNET_new (struct TALER_Extensions);
      86            0 :       extn->extension = extension;
      87            0 :       last->next = extn;
      88              :     }
      89              :   }
      90              : 
      91           50 :   return GNUNET_OK;
      92              : }
      93              : 
      94              : 
      95              : const struct TALER_Extension *
      96          108 : TALER_extensions_get_by_type (
      97              :   enum TALER_Extension_Type type)
      98              : {
      99          108 :   for (const struct TALER_Extensions *it = &TE_extensions;
     100          108 :        NULL != it && NULL != it->extension;
     101            0 :        it = it->next)
     102              :   {
     103           83 :     if (it->extension->type == type)
     104           83 :       return it->extension;
     105              :   }
     106              : 
     107              :   /* No extension found. */
     108           25 :   return NULL;
     109              : }
     110              : 
     111              : 
     112              : bool
     113            0 : TALER_extensions_is_enabled_type (
     114              :   enum TALER_Extension_Type type)
     115              : {
     116              :   const struct TALER_Extension *ext =
     117            0 :     TALER_extensions_get_by_type (type);
     118              : 
     119            0 :   return (NULL != ext && ext->enabled);
     120              : }
     121              : 
     122              : 
     123              : const struct TALER_Extension *
     124            0 : TALER_extensions_get_by_name (
     125              :   const char *name)
     126              : {
     127            0 :   for (const struct TALER_Extensions *it = &TE_extensions;
     128            0 :        NULL != it;
     129            0 :        it = it->next)
     130              :   {
     131            0 :     if (0 == strcasecmp (name, it->extension->name))
     132            0 :       return it->extension;
     133              :   }
     134              :   /* No extension found, try to load it. */
     135              : 
     136            0 :   return NULL;
     137              : }
     138              : 
     139              : 
     140              : enum GNUNET_GenericReturnValue
     141            0 : TALER_extensions_verify_manifests_signature (
     142              :   const json_t *manifests,
     143              :   struct TALER_MasterSignatureP *extensions_sig,
     144              :   struct TALER_MasterPublicKeyP *master_pub)
     145              : {
     146              :   struct TALER_ExtensionManifestsHashP h_manifests;
     147              : 
     148            0 :   if (GNUNET_OK !=
     149            0 :       TALER_JSON_extensions_manifests_hash (manifests,
     150              :                                             &h_manifests))
     151            0 :     return GNUNET_SYSERR;
     152            0 :   if (GNUNET_OK !=
     153            0 :       TALER_exchange_offline_extension_manifests_hash_verify (
     154              :         &h_manifests,
     155              :         master_pub,
     156              :         extensions_sig))
     157            0 :     return GNUNET_NO;
     158            0 :   return GNUNET_OK;
     159              : }
     160              : 
     161              : 
     162              : /**
     163              :  * Closure used in TALER_extensions_load_taler_config during call to
     164              :  * GNUNET_CONFIGURATION_iterate_sections with configure_extension.
     165              :  */
     166              : struct LoadConfClosure
     167              : {
     168              :   const struct GNUNET_CONFIGURATION_Handle *cfg;
     169              :   enum GNUNET_GenericReturnValue error;
     170              : };
     171              : 
     172              : 
     173              : /**
     174              :  * Used in TALER_extensions_load_taler_config during call to
     175              :  * GNUNET_CONFIGURATION_iterate_sections to load the configuration
     176              :  * of supported extensions.
     177              :  *
     178              :  * @param cls Closure of type LoadConfClosure
     179              :  * @param section name of the current section
     180              :  */
     181              : static void
     182         3806 : configure_extension (
     183              :   void *cls,
     184              :   const char *section)
     185              : {
     186         3806 :   struct LoadConfClosure *col = cls;
     187              :   const char *name;
     188         3806 :   char lib_name[1024] = {0};
     189              :   struct TALER_Extension *extension;
     190              : 
     191         3806 :   if (GNUNET_OK != col->error)
     192         3756 :     return;
     193              : 
     194         3806 :   if (0 != strncasecmp (section,
     195              :                         TALER_EXTENSION_SECTION_PREFIX,
     196              :                         sizeof(TALER_EXTENSION_SECTION_PREFIX) - 1))
     197         3756 :     return;
     198              : 
     199           50 :   name = section + sizeof(TALER_EXTENSION_SECTION_PREFIX) - 1;
     200              : 
     201              : 
     202              :   /* Load the extension library */
     203           50 :   GNUNET_snprintf (lib_name,
     204              :                    sizeof(lib_name),
     205              :                    "libtaler_extension_%s",
     206              :                    name);
     207              :   /* Lower-case extension name, config is case-insensitive */
     208         1750 :   for (unsigned int i = 0; i < strlen (lib_name); i++)
     209         1700 :     lib_name[i] = tolower (lib_name[i]);
     210              : 
     211           50 :   extension = GNUNET_PLUGIN_load (TALER_EXCHANGE_project_data (),
     212              :                                   lib_name,
     213           50 :                                   (void *) col->cfg);
     214           50 :   if (NULL == extension)
     215              :   {
     216            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     217              :                 "Couldn't load extension library to `%s` (section [%s]).\n",
     218              :                 name,
     219              :                 section);
     220            0 :     col->error = GNUNET_SYSERR;
     221            0 :     return;
     222              :   }
     223              : 
     224              : 
     225           50 :   if (GNUNET_OK != add_extension (extension))
     226              :   {
     227              :     /* FIXME[oec]: Ignoring return values here */
     228            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     229              :                 "Couldn't add extension `%s` (section [%s]).\n",
     230              :                 name,
     231              :                 section);
     232            0 :     col->error = GNUNET_SYSERR;
     233            0 :     GNUNET_PLUGIN_unload (
     234              :       lib_name,
     235            0 :       (void *) col->cfg);
     236            0 :     return;
     237              :   }
     238              : 
     239           50 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     240              :               "extension library '%s' loaded\n",
     241              :               lib_name);
     242              : }
     243              : 
     244              : 
     245              : static bool extensions_loaded = false;
     246              : 
     247              : enum GNUNET_GenericReturnValue
     248           77 : TALER_extensions_init (
     249              :   const struct GNUNET_CONFIGURATION_Handle *cfg)
     250              : {
     251           77 :   struct LoadConfClosure col = {
     252              :     .cfg = cfg,
     253              :     .error = GNUNET_OK,
     254              :   };
     255              : 
     256           77 :   if (extensions_loaded)
     257            0 :     return GNUNET_OK;
     258              : 
     259           77 :   GNUNET_CONFIGURATION_iterate_sections (cfg,
     260              :                                          &configure_extension,
     261              :                                          &col);
     262              : 
     263           77 :   if (GNUNET_OK == col.error)
     264           77 :     extensions_loaded = true;
     265              : 
     266           77 :   return col.error;
     267              : }
     268              : 
     269              : 
     270              : enum GNUNET_GenericReturnValue
     271            0 : TALER_extensions_parse_manifest (
     272              :   json_t *obj,
     273              :   int *critical,
     274              :   const char **version,
     275              :   json_t **config)
     276              : {
     277              :   enum GNUNET_GenericReturnValue ret;
     278              :   struct GNUNET_JSON_Specification spec[] = {
     279            0 :     GNUNET_JSON_spec_boolean ("critical",
     280              :                               critical),
     281            0 :     GNUNET_JSON_spec_string ("version",
     282              :                              version),
     283            0 :     GNUNET_JSON_spec_json ("config",
     284              :                            config),
     285            0 :     GNUNET_JSON_spec_end ()
     286              :   };
     287              : 
     288            0 :   *config = NULL;
     289            0 :   if (GNUNET_OK !=
     290            0 :       (ret = GNUNET_JSON_parse (obj,
     291              :                                 spec,
     292              :                                 NULL,
     293              :                                 NULL)))
     294            0 :     return ret;
     295            0 :   return GNUNET_OK;
     296              : }
     297              : 
     298              : 
     299              : enum GNUNET_GenericReturnValue
     300            0 : TALER_extensions_load_manifests (
     301              :   const json_t *extensions)
     302              : {
     303              :   const char *name;
     304              :   json_t *manifest;
     305              : 
     306            0 :   GNUNET_assert (NULL != extensions);
     307            0 :   GNUNET_assert (json_is_object (extensions));
     308              : 
     309            0 :   json_object_foreach ((json_t *) extensions, name, manifest)
     310              :   {
     311              :     int critical;
     312              :     const char *version;
     313              :     json_t *config;
     314              :     struct TALER_Extension *extension
     315              :       = (struct TALER_Extension *)
     316            0 :         TALER_extensions_get_by_name (name);
     317              : 
     318            0 :     if (NULL == extension)
     319              :     {
     320            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     321              :                   "no such extension: %s\n",
     322              :                   name);
     323            0 :       return GNUNET_SYSERR;
     324              :     }
     325              : 
     326              :     /* load and verify criticality, version, etc. */
     327            0 :     if (GNUNET_OK !=
     328            0 :         TALER_extensions_parse_manifest (
     329              :           manifest,
     330              :           &critical,
     331              :           &version,
     332              :           &config))
     333            0 :       return GNUNET_SYSERR;
     334              : 
     335            0 :     if (critical != extension->critical
     336            0 :         || 0 != strcmp (version,
     337              :                         extension->version) // FIXME[oec]: libtool compare?
     338            0 :         || NULL == config
     339            0 :         || (GNUNET_OK !=
     340            0 :             extension->load_config (config,
     341              :                                     NULL)) )
     342            0 :       return GNUNET_SYSERR;
     343              : 
     344              :     /* This _should_ work now */
     345            0 :     if (GNUNET_OK !=
     346            0 :         extension->load_config (config,
     347              :                                 extension))
     348            0 :       return GNUNET_SYSERR;
     349              : 
     350            0 :     extension->enabled = true;
     351              :   }
     352              : 
     353              :   /* make sure to disable all extensions that weren't mentioned in the json */
     354            0 :   for (const struct TALER_Extensions *it = TALER_extensions_get_head ();
     355            0 :        NULL != it;
     356            0 :        it = it->next)
     357              :   {
     358            0 :     if (NULL == json_object_get (extensions, it->extension->name))
     359            0 :       it->extension->disable ((struct TALER_Extension *) it);
     360              :   }
     361              : 
     362            0 :   return GNUNET_OK;
     363              : }
     364              : 
     365              : 
     366              : /**
     367              :  * Policy related
     368              :  */
     369              : static const char *fulfillment2str[] =  {
     370              :   [TALER_PolicyFulfillmentInitial]      = "<init>",
     371              :   [TALER_PolicyFulfillmentReady]        = "Ready",
     372              :   [TALER_PolicyFulfillmentSuccess]      = "Success",
     373              :   [TALER_PolicyFulfillmentFailure]      = "Failure",
     374              :   [TALER_PolicyFulfillmentTimeout]      = "Timeout",
     375              :   [TALER_PolicyFulfillmentInsufficient] = "Insufficient",
     376              : };
     377              : 
     378              : const char *
     379            0 : TALER_policy_fulfillment_state_str (
     380              :   enum TALER_PolicyFulfillmentState state)
     381              : {
     382            0 :   GNUNET_assert (TALER_PolicyFulfillmentStateCount > state);
     383            0 :   return fulfillment2str[state];
     384              : }
     385              : 
     386              : 
     387              : enum GNUNET_GenericReturnValue
     388            0 : TALER_extensions_create_policy_details (
     389              :   const char *currency,
     390              :   const json_t *policy_options,
     391              :   struct TALER_PolicyDetails *details,
     392              :   const char **error_hint)
     393              : {
     394              :   enum GNUNET_GenericReturnValue ret;
     395              :   const struct TALER_Extension *extension;
     396              :   const json_t *jtype;
     397              :   const char *type;
     398              : 
     399            0 :   *error_hint = NULL;
     400              : 
     401            0 :   if ((NULL == policy_options) ||
     402            0 :       (! json_is_object (policy_options)))
     403              :   {
     404            0 :     *error_hint = "invalid policy object";
     405            0 :     return GNUNET_SYSERR;
     406              :   }
     407              : 
     408            0 :   jtype = json_object_get (policy_options, "type");
     409            0 :   if (NULL == jtype)
     410              :   {
     411            0 :     *error_hint = "no type in policy object";
     412            0 :     return GNUNET_SYSERR;
     413              :   }
     414              : 
     415            0 :   type = json_string_value (jtype);
     416            0 :   if (NULL == type)
     417              :   {
     418            0 :     *error_hint = "invalid type in policy object";
     419            0 :     return GNUNET_SYSERR;
     420              :   }
     421              : 
     422            0 :   extension = TALER_extensions_get_by_name (type);
     423            0 :   if ((NULL == extension) ||
     424            0 :       (NULL == extension->create_policy_details))
     425              :   {
     426            0 :     GNUNET_break (0);
     427            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     428              :                 "Unsupported extension policy '%s' requested\n",
     429              :                 type);
     430            0 :     return GNUNET_NO;
     431              :   }
     432              : 
     433              :   /* Set state fields in the policy details to initial values. */
     434            0 :   GNUNET_assert (GNUNET_OK ==
     435              :                  TALER_amount_set_zero (currency,
     436              :                                         &details->accumulated_total));
     437            0 :   GNUNET_assert (GNUNET_OK ==
     438              :                  TALER_amount_set_zero (currency,
     439              :                                         &details->policy_fee));
     440            0 :   details->deadline = GNUNET_TIME_UNIT_FOREVER_TS;
     441            0 :   details->fulfillment_state = TALER_PolicyFulfillmentInitial;
     442            0 :   details->no_policy_fulfillment_id = true;
     443            0 :   ret = extension->create_policy_details (currency,
     444              :                                           policy_options,
     445              :                                           details,
     446              :                                           error_hint);
     447            0 :   return ret;
     448              : 
     449              : }
     450              : 
     451              : 
     452              : /* end of extensions.c */
        

Generated by: LCOV version 2.0-1