merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

merchant_api_get_config.c (9781B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2014-2025 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Lesser General Public License as published by the Free Software
      7   Foundation; either version 2.1, 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 Lesser General Public License for more details.
     12 
     13   You should have received a copy of the GNU Lesser General Public License along with
     14   TALER; see the file COPYING.LGPL.  If not, see
     15   <http://www.gnu.org/licenses/>
     16 */
     17 /**
     18  * @file merchant_api_get_config.c
     19  * @brief Implementation of the /config request of the merchant's HTTP API
     20  * @author Christian Grothoff
     21  */
     22 #include "platform.h"
     23 #include <curl/curl.h>
     24 #include <jansson.h>
     25 #include <microhttpd.h> /* just for HTTP status codes */
     26 #include <gnunet/gnunet_util_lib.h>
     27 #include <gnunet/gnunet_curl_lib.h>
     28 #include "taler_merchant_service.h"
     29 #include "merchant_api_curl_defaults.h"
     30 #include <taler/taler_json_lib.h>
     31 #include <taler/taler_signatures.h>
     32 
     33 /**
     34  * Which version of the Taler protocol is implemented
     35  * by this library?  Used to determine compatibility.
     36  */
     37 #define MERCHANT_PROTOCOL_CURRENT 24
     38 
     39 /**
     40  * How many configs are we backwards-compatible with?
     41  */
     42 #define MERCHANT_PROTOCOL_AGE 0
     43 
     44 /**
     45  * How many exchanges do we allow at most per merchant?
     46  */
     47 #define MAX_EXCHANGES 1024
     48 
     49 /**
     50  * How many currency specs do we allow at most per merchant?
     51  */
     52 #define MAX_CURRENCIES 1024
     53 
     54 /**
     55  * @brief A handle for /config operations
     56  */
     57 struct TALER_MERCHANT_ConfigGetHandle
     58 {
     59   /**
     60    * The url for this request.
     61    */
     62   char *url;
     63 
     64   /**
     65    * Handle for the request.
     66    */
     67   struct GNUNET_CURL_Job *job;
     68 
     69   /**
     70    * Function to call with the result.
     71    */
     72   TALER_MERCHANT_ConfigCallback cb;
     73 
     74   /**
     75    * Closure for @a cb.
     76    */
     77   void *cb_cls;
     78 
     79   /**
     80    * Reference to the execution context.
     81    */
     82   struct GNUNET_CURL_Context *ctx;
     83 
     84 };
     85 
     86 
     87 /**
     88  * Function called when we're done processing the
     89  * HTTP /config request.
     90  *
     91  * @param cls the `struct TALER_MERCHANT_ConfigGetHandle`
     92  * @param response_code HTTP response code, 0 on error
     93  * @param response response body, NULL if not in JSON
     94  */
     95 static void
     96 handle_config_finished (void *cls,
     97                         long response_code,
     98                         const void *response)
     99 {
    100   struct TALER_MERCHANT_ConfigGetHandle *vgh = cls;
    101   const json_t *json = response;
    102   struct TALER_MERCHANT_ConfigResponse cr = {
    103     .hr.http_status = (unsigned int) response_code,
    104     .hr.reply = json
    105   };
    106 
    107   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    108               "Got /config response with status code %u\n",
    109               (unsigned int) response_code);
    110 
    111   vgh->job = NULL;
    112   switch (response_code)
    113   {
    114   case MHD_HTTP_OK:
    115     {
    116       const json_t *jcs;
    117       const json_t *exchanges = NULL;
    118       struct TALER_MERCHANT_ExchangeConfigInfo *eci = NULL;
    119       unsigned int num_eci = 0;
    120       unsigned int nspec;
    121       struct TALER_JSON_ProtocolVersion pv;
    122       struct GNUNET_JSON_Specification spec[] = {
    123         GNUNET_JSON_spec_object_const ("currencies",
    124                                        &jcs),
    125         GNUNET_JSON_spec_array_const ("exchanges",
    126                                       &exchanges),
    127         GNUNET_JSON_spec_string ("currency",
    128                                  &cr.details.ok.ci.currency),
    129         TALER_JSON_spec_version ("version",
    130                                  &pv),
    131         GNUNET_JSON_spec_string ("version",
    132                                  &cr.details.ok.ci.version),
    133         GNUNET_JSON_spec_end ()
    134       };
    135 
    136       cr.details.ok.compat = TALER_MERCHANT_VC_PROTOCOL_ERROR;
    137       if (GNUNET_OK !=
    138           GNUNET_JSON_parse (json,
    139                              spec,
    140                              NULL, NULL))
    141       {
    142         GNUNET_break_op (0);
    143         cr.hr.http_status = 0;
    144         cr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    145         break;
    146       }
    147       cr.details.ok.compat = TALER_MERCHANT_VC_MATCH;
    148       if (MERCHANT_PROTOCOL_CURRENT < pv.current)
    149       {
    150         cr.details.ok.compat |= TALER_MERCHANT_VC_NEWER;
    151         if (MERCHANT_PROTOCOL_CURRENT < pv.current - pv.age)
    152           cr.details.ok.compat |= TALER_MERCHANT_VC_INCOMPATIBLE;
    153       }
    154       if (MERCHANT_PROTOCOL_CURRENT > pv.current)
    155       {
    156         cr.details.ok.compat |= TALER_MERCHANT_VC_OLDER;
    157         if (MERCHANT_PROTOCOL_CURRENT - MERCHANT_PROTOCOL_AGE > pv.current)
    158           cr.details.ok.compat |= TALER_MERCHANT_VC_INCOMPATIBLE;
    159       }
    160 
    161       nspec = (unsigned int) json_object_size (jcs);
    162       if ( (nspec > MAX_CURRENCIES) ||
    163            (json_object_size (jcs) != (size_t) nspec) )
    164       {
    165         GNUNET_break_op (0);
    166         cr.hr.http_status = 0;
    167         cr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    168         break;
    169       }
    170       if (NULL != exchanges)
    171       {
    172         num_eci = (unsigned int) json_object_size (exchanges);
    173         if ( (num_eci > MAX_EXCHANGES) ||
    174              (json_object_size (exchanges) != (size_t) num_eci) )
    175         {
    176           GNUNET_break_op (0);
    177           cr.hr.http_status = 0;
    178           cr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    179           break;
    180         }
    181         eci = GNUNET_new_array (num_eci,
    182                                 struct TALER_MERCHANT_ExchangeConfigInfo);
    183         for (unsigned int i = 0; i<num_eci; i++)
    184         {
    185           struct TALER_MERCHANT_ExchangeConfigInfo *ei = &eci[i];
    186           const json_t *ej = json_array_get (exchanges,
    187                                              i);
    188           struct GNUNET_JSON_Specification ispec[] = {
    189             GNUNET_JSON_spec_string ("currency",
    190                                      &ei->currency),
    191             GNUNET_JSON_spec_string ("base_url",
    192                                      &ei->base_url),
    193             GNUNET_JSON_spec_fixed_auto ("master_pub",
    194                                          &ei->master_pub),
    195             GNUNET_JSON_spec_end ()
    196           };
    197 
    198           if (GNUNET_OK !=
    199               GNUNET_JSON_parse (ej,
    200                                  ispec,
    201                                  NULL, NULL))
    202           {
    203             GNUNET_break_op (0);
    204             cr.hr.http_status = 0;
    205             cr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    206             GNUNET_free (eci);
    207             break;
    208           }
    209         }
    210       }
    211       {
    212         struct TALER_CurrencySpecification *cspecs;
    213         unsigned int off = 0;
    214         json_t *obj;
    215         const char *curr;
    216 
    217         cspecs = GNUNET_new_array (nspec,
    218                                    struct TALER_CurrencySpecification);
    219         cr.details.ok.num_cspecs = nspec;
    220         cr.details.ok.cspecs = cspecs;
    221         cr.details.ok.num_exchanges = (unsigned int) num_eci;
    222         cr.details.ok.exchanges = eci;
    223         json_object_foreach ((json_t *) jcs, curr, obj)
    224         {
    225           struct TALER_CurrencySpecification *cs = &cspecs[off++];
    226           struct GNUNET_JSON_Specification cspec[] = {
    227             TALER_JSON_spec_currency_specification (curr,
    228                                                     curr,
    229                                                     cs),
    230             GNUNET_JSON_spec_end ()
    231           };
    232 
    233           if (GNUNET_OK !=
    234               GNUNET_JSON_parse (jcs,
    235                                  cspec,
    236                                  NULL, NULL))
    237           {
    238             GNUNET_break_op (0);
    239             cr.hr.http_status = 0;
    240             cr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    241             GNUNET_free (eci);
    242             TALER_CONFIG_free_currencies (off - 1,
    243                                           cspecs);
    244             break;
    245           }
    246         }
    247         vgh->cb (vgh->cb_cls,
    248                  &cr);
    249         GNUNET_free (eci);
    250         TALER_CONFIG_free_currencies (nspec,
    251                                       cspecs);
    252       }
    253       TALER_MERCHANT_config_get_cancel (vgh);
    254       return;
    255     }
    256   default:
    257     /* unexpected response code */
    258     cr.hr.ec = TALER_JSON_get_error_code (json);
    259     cr.hr.hint = TALER_JSON_get_error_hint (json);
    260     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    261                 "Unexpected response code %u/%d\n",
    262                 (unsigned int) response_code,
    263                 (int) cr.hr.ec);
    264     break;
    265   }
    266   vgh->cb (vgh->cb_cls,
    267            &cr);
    268   TALER_MERCHANT_config_get_cancel (vgh);
    269 }
    270 
    271 
    272 struct TALER_MERCHANT_ConfigGetHandle *
    273 TALER_MERCHANT_config_get (struct GNUNET_CURL_Context *ctx,
    274                            const char *backend_url,
    275                            TALER_MERCHANT_ConfigCallback config_cb,
    276                            void *config_cb_cls)
    277 {
    278   struct TALER_MERCHANT_ConfigGetHandle *vgh;
    279   CURL *eh;
    280 
    281   vgh = GNUNET_new (struct TALER_MERCHANT_ConfigGetHandle);
    282   vgh->ctx = ctx;
    283   vgh->cb = config_cb;
    284   vgh->cb_cls = config_cb_cls;
    285   vgh->url = TALER_url_join (backend_url,
    286                              "config",
    287                              NULL);
    288   if (NULL == vgh->url)
    289   {
    290     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    291                 "Could not construct request URL.\n");
    292     GNUNET_free (vgh);
    293     return NULL;
    294   }
    295   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    296               "Requesting URL '%s'\n",
    297               vgh->url);
    298   eh = TALER_MERCHANT_curl_easy_get_ (vgh->url);
    299   vgh->job = GNUNET_CURL_job_add (ctx,
    300                                   eh,
    301                                   &handle_config_finished,
    302                                   vgh);
    303   return vgh;
    304 }
    305 
    306 
    307 void
    308 TALER_MERCHANT_config_get_cancel (struct TALER_MERCHANT_ConfigGetHandle *vgh)
    309 {
    310   if (NULL != vgh->job)
    311   {
    312     GNUNET_CURL_job_cancel (vgh->job);
    313     vgh->job = NULL;
    314   }
    315   GNUNET_free (vgh->url);
    316   GNUNET_free (vgh);
    317 }
    318 
    319 
    320 /* end of merchant_api_config_get.c */