frosix

Multiparty signature service (experimental)
Log | Files | Refs | README | LICENSE

frosix_api_config.c (8198B)


      1 /*
      2   This file is part of Frosix
      3   Copyright (C) 2020, 2021 Anastasis SARL
      4 
      5   Frosix 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   Frosix 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   Frosix; see the file COPYING.GPL.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file restclient/frosix_api_config.c
     18  * @brief Implementation of the /config GET
     19  * @author Christian Grothoff
     20  * @author Dennis Neufeld
     21  * @author Dominik Meister
     22  */
     23 #include "platform.h"
     24 #include <curl/curl.h>
     25 #include <microhttpd.h> /* just for HTTP status codes */
     26 #include "frosix_service.h"
     27 #include "frosix_api_curl_defaults.h"
     28 #include <gnunet/gnunet_json_lib.h>
     29 #include <taler/taler_json_lib.h>
     30 
     31 
     32 /**
     33  * Which version of the Taler protocol is implemented
     34  * by this library?  Used to determine compatibility.
     35  */
     36 #define FROSIX_PROTOCOL_CURRENT 0
     37 
     38 /**
     39  * How many versions are we backwards compatible with?
     40  */
     41 #define FROSIX_PROTOCOL_AGE 0
     42 
     43 
     44 
     45 /**
     46  * Function called when we're done processing the
     47  * HTTP /config request.
     48  *
     49  * @param cls the `struct FROSIX_ConfigOperation`
     50  * @param response_code HTTP response code, 0 on error
     51  * @param response parsed JSON result, NULL on error
     52  */
     53 static void
     54 handle_config_finished (void *cls,
     55                         long response_code,
     56                         const void *response)
     57 {
     58   struct FROSIX_ConfigOperation *co = cls;
     59   const json_t *json = response;
     60 
     61   co->job = NULL;
     62 
     63   switch (response_code)
     64   {
     65   case 0:
     66     /* No reply received */
     67     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     68                 "Backend `%s' failed to respond to GET /config\n",
     69                 co->url);
     70     break;
     71   case MHD_HTTP_OK:
     72     {
     73       const char *name;
     74       struct FROSIX_Config fcfg;
     75       fcfg.provider_index = co->provider_index;
     76       json_t *methods;
     77       struct GNUNET_JSON_Specification spec[] = {
     78         GNUNET_JSON_spec_string ("name",
     79                                  &name),
     80         GNUNET_JSON_spec_string ("business_name",
     81                                  &fcfg.business_name),
     82         GNUNET_JSON_spec_string ("version",
     83                                  &fcfg.version),
     84         GNUNET_JSON_spec_json ("methods",
     85                                &methods),
     86         TALER_JSON_spec_amount_any ("annual_fee",
     87                                     &fcfg.annual_fee),
     88         TALER_JSON_spec_amount_any ("signature_creation_fee",
     89                                     &fcfg.signature_creation_fee),
     90         GNUNET_JSON_spec_fixed_auto ("provider_salt",
     91                                      &fcfg.provider_salt),
     92         GNUNET_JSON_spec_fixed_auto ("public_key",
     93                                      &fcfg.public_key),
     94         GNUNET_JSON_spec_end ()
     95       };
     96 
     97       if (GNUNET_OK !=
     98           GNUNET_JSON_parse (json,
     99                              spec,
    100                              NULL, NULL))
    101       {
    102         GNUNET_break_op (0);
    103         json_dumpf (json,
    104                     stderr,
    105                     JSON_INDENT (2));
    106         response_code = 0;
    107         break;
    108       }
    109       if (0 != strcmp (name,
    110                        "frosix"))
    111       {
    112         GNUNET_JSON_parse_free (spec);
    113         response_code = 0;
    114         break;
    115       }
    116       {
    117         unsigned int age;
    118         unsigned int revision;
    119         unsigned int current;
    120         char dummy;
    121 
    122         if (3 != sscanf (fcfg.version,
    123                          "%u:%u:%u%c",
    124                          &current,
    125                          &revision,
    126                          &age,
    127                          &dummy))
    128         {
    129           GNUNET_break_op (0);
    130           response_code = 0;
    131           GNUNET_JSON_parse_free (spec);
    132           break;
    133         }
    134         if ( (FROSIX_PROTOCOL_CURRENT < current) &&
    135              (FROSIX_PROTOCOL_CURRENT < current - age) )
    136         {
    137           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    138                       "Provider protocol version too new\n");
    139           response_code = 0;
    140           GNUNET_JSON_parse_free (spec);
    141           break;
    142         }
    143         if ( (FROSIX_PROTOCOL_CURRENT > current) &&
    144              (FROSIX_PROTOCOL_CURRENT - FROSIX_PROTOCOL_AGE > current) )
    145         {
    146           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    147                       "Provider protocol version too old\n");
    148           GNUNET_break_op (0);
    149           response_code = 0;
    150           GNUNET_JSON_parse_free (spec);
    151           break;
    152         }
    153       }
    154       if (! json_is_array (methods))
    155       {
    156         GNUNET_break_op (0);
    157         GNUNET_JSON_parse_free (spec);
    158         response_code = 0;
    159         break;
    160       }
    161       fcfg.methods_length = json_array_size (methods);
    162       {
    163         struct FROSIX_AuthorizationMethodConfig mcfg[GNUNET_NZL (
    164                                                        fcfg.methods_length)];
    165 
    166         for (unsigned int i = 0; i<fcfg.methods_length; i++)
    167         {
    168           struct FROSIX_AuthorizationMethodConfig *m = &mcfg[i];
    169           struct GNUNET_JSON_Specification spec[] = {
    170             GNUNET_JSON_spec_string ("type",
    171                                      &m->type),
    172             TALER_JSON_spec_amount_any ("cost",
    173                                         &m->usage_fee),
    174             GNUNET_JSON_spec_end ()
    175           };
    176 
    177           if ( (GNUNET_OK !=
    178                 GNUNET_JSON_parse (json_array_get (methods,
    179                                                    i),
    180                                    spec,
    181                                    NULL, NULL)) )
    182           {
    183             GNUNET_break_op (0);
    184             GNUNET_JSON_parse_free (spec);
    185             response_code = 0;
    186             goto end;
    187           }
    188         }
    189         fcfg.methods = mcfg;
    190         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    191                     "Good backend found at `%s'\n",
    192                     co->url);
    193         co->cb (co->cb_cls,
    194                 MHD_HTTP_OK,
    195                 &fcfg);
    196         GNUNET_JSON_parse_free (spec);
    197         FROSIX_config_cancel (co);
    198         return;
    199       }
    200     }
    201   case MHD_HTTP_BAD_REQUEST:
    202     /* This should never happen, either us or the anastasis server is buggy
    203        (or API version conflict); just pass JSON reply to the application */
    204     break;
    205   case MHD_HTTP_NOT_FOUND:
    206     /* Nothing really to verify */
    207     break;
    208   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    209     /* Server had an internal issue; we should retry, but this API
    210        leaves this to the application */
    211     break;
    212   default:
    213     /* unexpected response code */
    214     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    215                 "Unexpected response code %u\n",
    216                 (unsigned int) response_code);
    217     GNUNET_break_op (0);
    218     break;
    219   }
    220   end:
    221   co->cb (co->cb_cls,
    222           response_code,
    223           NULL);
    224   FROSIX_config_cancel (co);
    225 }
    226 
    227 
    228 struct FROSIX_ConfigOperation *
    229 FROSIX_get_config (struct GNUNET_CURL_Context *ctx,
    230                    const char *base_url,
    231                    uint8_t provider_index,
    232                    FROSIX_ConfigCallback cb,
    233                    void *cb_cls)
    234 {
    235   struct FROSIX_ConfigOperation *co;
    236 
    237   co = GNUNET_new (struct FROSIX_ConfigOperation);
    238   co->url = TALER_url_join (base_url,
    239                             "config",
    240                             NULL);
    241   co->provider_index = provider_index;
    242   co->ctx = ctx;
    243   co->cb = cb;
    244   co->cb_cls = cb_cls;
    245   {
    246     CURL *eh;
    247     eh = FROSIX_curl_easy_get_ (co->url);
    248     co->job = GNUNET_CURL_job_add (ctx,
    249                                    eh,
    250                                    &handle_config_finished,
    251                                    co);
    252   }
    253 
    254   if (NULL == co->job)
    255   {
    256     GNUNET_free (co->url);
    257     GNUNET_free (co);
    258     return NULL;
    259   }
    260 
    261   return co;
    262 }
    263 
    264 
    265 void
    266 FROSIX_config_cancel (struct FROSIX_ConfigOperation *co)
    267 {
    268   if (NULL != co->job)
    269   {
    270     GNUNET_CURL_job_cancel (co->job);
    271     co->job = NULL;
    272   }
    273   GNUNET_free (co->url);
    274   GNUNET_free (co);
    275 }
    276 
    277 
    278 /* end of frosix_api_config.c */