donau

Donation authority for GNU Taler (experimental)
Log | Files | Refs | Submodules | README | LICENSE

donau_api_charities_get.c (8086B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2024 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it
      6   under the terms of the GNU General Public License as published
      7   by the Free Software Foundation; either version 3, or (at your
      8   option) any later version.
      9 
     10   TALER is distributed in the hope that it will be useful, but
     11   WITHOUT ANY WARRANTY; without even the implied warranty of
     12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13   GNU General Public License for more details.
     14 
     15   You should have received a copy of the GNU General Public
     16   License along with TALER; see the file COPYING.  If not, see
     17   <http://www.gnu.org/licenses/>
     18 */
     19 
     20 /**
     21  * @file lib/donau_api_charities_get.c
     22  * @brief Implementation of the "handle" component of the donau's HTTP API
     23  * @author Lukas Matyja
     24  */
     25 #include <gnunet/gnunet_curl_lib.h>
     26 #include <taler/taler_json_lib.h>
     27 #include "donau_service.h"
     28 #include "donau_api_curl_defaults.h"
     29 #include "donau_json_lib.h"
     30 
     31 
     32 /**
     33  * Handle for a GET /charities request.
     34  */
     35 struct DONAU_CharitiesGetHandle
     36 {
     37   /**
     38    * The url for the /charities request.
     39    */
     40   char *url;
     41 
     42   /**
     43    * Entry for this request with the `struct GNUNET_CURL_Context`.
     44    */
     45   struct GNUNET_CURL_Job *job;
     46 
     47   /**
     48    * Function to call with the result.
     49    */
     50   DONAU_GetCharitiesResponseCallback cb;
     51 
     52   /**
     53    * Closure to pass to @e cb.
     54    */
     55   void *cb_cls;
     56 
     57 };
     58 
     59 /**
     60  * Decode the JSON in @a resp_obj from the /charities response
     61  * and store the data in the @a charities_data.
     62  *
     63  * @param[in] resp_obj JSON object to parse
     64  * @param[in] cgh contains the callback function
     65  * @param[out] gcresp where to store the results we decoded
     66  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
     67  * (malformed JSON)
     68  */
     69 static enum GNUNET_GenericReturnValue
     70 handle_charities_get_ok (const json_t *resp_obj,
     71                          struct DONAU_CharitiesGetHandle *cgh,
     72                          struct DONAU_GetCharitiesResponse *gcresp)
     73 {
     74   struct DONAU_CharitySummary *charities = NULL;
     75   const json_t *ca = json_object_get (resp_obj,
     76                                       "charities");
     77   size_t num_charity;
     78 
     79   if ( (NULL == ca) ||
     80        (! json_is_array (ca)) )
     81   {
     82     GNUNET_break_op (0);
     83     return GNUNET_SYSERR;
     84   }
     85   num_charity = json_array_size (ca);
     86   if (0 != num_charity)
     87   {
     88     size_t index;
     89     json_t *charity_obj;
     90 
     91     charities = GNUNET_new_array (num_charity,
     92                                   struct DONAU_CharitySummary);
     93     json_array_foreach (ca,
     94                         index,
     95                         charity_obj)
     96     {
     97       struct GNUNET_JSON_Specification spec[] = {
     98         GNUNET_JSON_spec_uint64 ("charity_id",
     99                                  &charities[index].charity_id),
    100         GNUNET_JSON_spec_string ("charity_name",
    101                                  &charities[index].name),
    102         TALER_JSON_spec_amount_any ("max_per_year",
    103                                     &charities[index].max_per_year),
    104         TALER_JSON_spec_amount_any ("receipts_to_date",
    105                                     &charities[index].receipts_to_date),
    106         GNUNET_JSON_spec_end ()
    107       };
    108 
    109       if (GNUNET_OK !=
    110           GNUNET_JSON_parse (charity_obj,
    111                              spec,
    112                              NULL,
    113                              NULL))
    114       {
    115         GNUNET_break_op (0);
    116         GNUNET_free (charities);
    117         return GNUNET_SYSERR;
    118       }
    119     }
    120     gcresp->details.ok.num_charities = num_charity;
    121     gcresp->details.ok.charities = charities;
    122   }
    123   cgh->cb (cgh->cb_cls,
    124            gcresp);
    125   cgh->cb = NULL;
    126   GNUNET_free (charities);
    127   return GNUNET_OK;
    128 }
    129 
    130 
    131 /**
    132  * Callback used when downloading the reply to a /charities request
    133  * is complete.
    134  *
    135  * @param cls the `struct KeysRequest`
    136  * @param response_code HTTP response code, 0 on error
    137  * @param resp_obj parsed JSON result, NULL on error
    138  */
    139 static void
    140 handle_charities_get_finished (void *cls,
    141                                long response_code,
    142                                const void *resp_obj)
    143 {
    144   struct DONAU_CharitiesGetHandle *cgh = cls;
    145   const json_t *j = resp_obj;
    146   struct DONAU_GetCharitiesResponse gcresp = {
    147     .hr.reply = j,
    148     .hr.http_status = (unsigned int) response_code
    149   };
    150 
    151   cgh->job = NULL;
    152   switch (response_code)
    153   {
    154   case 0:
    155     gcresp.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    156     break;
    157   case MHD_HTTP_OK:
    158     if (GNUNET_OK !=
    159         handle_charities_get_ok (j,
    160                                  cgh,
    161                                  &gcresp))
    162     {
    163       gcresp.hr.http_status = 0;
    164       gcresp.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
    165     }
    166     break;
    167   case MHD_HTTP_BAD_REQUEST:
    168     /* This should never happen, either us or the donau is buggy
    169        (or API version conflict); just pass JSON reply to the application */
    170     gcresp.hr.ec = TALER_JSON_get_error_code (j);
    171     gcresp.hr.hint = TALER_JSON_get_error_hint (j);
    172     break;
    173   case MHD_HTTP_FORBIDDEN:
    174     /* Nothing really to verify */
    175     gcresp.hr.ec = TALER_JSON_get_error_code (j);
    176     gcresp.hr.hint = TALER_JSON_get_error_hint (j);
    177     break;
    178   case MHD_HTTP_NOT_FOUND:
    179     /* Nothing really to verify, this should never
    180        happen, we should pass the JSON reply to the application */
    181     gcresp.hr.ec = TALER_JSON_get_error_code (j);
    182     gcresp.hr.hint = TALER_JSON_get_error_hint (j);
    183     break;
    184   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    185     /* Server had an internal issue; we should retry, but this API
    186        leaves this to the application */
    187     gcresp.hr.ec = TALER_JSON_get_error_code (j);
    188     gcresp.hr.hint = TALER_JSON_get_error_hint (j);
    189     break;
    190   default:
    191     /* unexpected response code */
    192     GNUNET_break_op (0);
    193     gcresp.hr.ec = TALER_JSON_get_error_code (j);
    194     gcresp.hr.hint = TALER_JSON_get_error_hint (j);
    195     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    196                 "Unexpected response code %u/%d for GET %s\n",
    197                 (unsigned int) response_code,
    198                 (int) gcresp.hr.ec,
    199                 cgh->url);
    200     break;
    201   }
    202 
    203   if (NULL != cgh->cb)
    204   {
    205     cgh->cb (cgh->cb_cls,
    206              &gcresp);
    207     cgh->cb = NULL;
    208   }
    209   DONAU_charities_get_cancel (cgh);
    210 }
    211 
    212 
    213 struct DONAU_CharitiesGetHandle *
    214 DONAU_charities_get (
    215   struct GNUNET_CURL_Context *ctx,
    216   const char *url,
    217   const struct DONAU_BearerToken *bearer, // FIXME-#9435: check authorization
    218   DONAU_GetCharitiesResponseCallback cb,
    219   void *cb_cls)
    220 {
    221   struct DONAU_CharitiesGetHandle *cgh;
    222   CURL *eh;
    223   const char *arg_str = "charities";
    224 
    225   TALER_LOG_DEBUG ("Connecting to the donau (%s)\n",
    226                    url);
    227   cgh = GNUNET_new (struct DONAU_CharitiesGetHandle);
    228   cgh->cb = cb;
    229   cgh->cb_cls = cb_cls;
    230   cgh->url = TALER_url_join (url,
    231                              arg_str,
    232                              NULL);
    233   if (NULL == cgh->url)
    234   {
    235     GNUNET_free (cgh);
    236     return NULL;
    237   }
    238   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    239               "Requesting all charities with URL `%s'.\n",
    240               cgh->url);
    241   eh = DONAU_curl_easy_get_ (cgh->url);
    242   if (NULL == eh)
    243   {
    244     GNUNET_break (0);
    245     GNUNET_free (cgh->url);
    246     GNUNET_free (cgh);
    247     return NULL;
    248   }
    249   cgh->job = GNUNET_CURL_job_add_with_ct_json (ctx,
    250                                                eh,
    251                                                &handle_charities_get_finished,
    252                                                cgh);
    253   GNUNET_assert (NULL != cgh->job);
    254   if (NULL != bearer)
    255   {
    256     struct curl_slist *auth;
    257     char *hdr;
    258 
    259     GNUNET_asprintf (&hdr,
    260                      "%s: Bearer %s",
    261                      MHD_HTTP_HEADER_AUTHORIZATION,
    262                      bearer->token);
    263     auth = curl_slist_append (NULL,
    264                               hdr);
    265     GNUNET_free (hdr);
    266     GNUNET_CURL_extend_headers (cgh->job,
    267                                 auth);
    268     curl_slist_free_all (auth);
    269   }
    270   return cgh;
    271 }
    272 
    273 
    274 void
    275 DONAU_charities_get_cancel (
    276   struct DONAU_CharitiesGetHandle *cgh)
    277 {
    278   if (NULL != cgh->job)
    279   {
    280     GNUNET_CURL_job_cancel (cgh->job);
    281     cgh->job = NULL;
    282   }
    283   GNUNET_free (cgh->url);
    284   GNUNET_free (cgh);
    285 }