donau

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

donau_api_charities_get.c (8059B)


      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     = json_array_size (ca);
     79 
     80   if (JSON_ARRAY != json_typeof (ca))
     81   {
     82     GNUNET_break_op (0);
     83     return GNUNET_SYSERR;
     84   }
     85   if (0 != num_charity)
     86   {
     87     size_t index;
     88     json_t *charity_obj;
     89 
     90     charities = GNUNET_new_array (num_charity,
     91                                   struct DONAU_CharitySummary);
     92     json_array_foreach (ca,
     93                         index,
     94                         charity_obj)
     95     {
     96       struct GNUNET_JSON_Specification spec[] = {
     97         GNUNET_JSON_spec_uint64 ("charity_id",
     98                                  &charities[index].charity_id),
     99         GNUNET_JSON_spec_string ("charity_name",
    100                                  &charities[index].name),
    101         TALER_JSON_spec_amount_any ("max_per_year",
    102                                     &charities[index].max_per_year),
    103         TALER_JSON_spec_amount_any ("receipts_to_date",
    104                                     &charities[index].receipts_to_date),
    105         GNUNET_JSON_spec_end ()
    106       };
    107 
    108       if (GNUNET_OK !=
    109           GNUNET_JSON_parse (charity_obj,
    110                              spec,
    111                              NULL,
    112                              NULL))
    113       {
    114         GNUNET_break_op (0);
    115         return GNUNET_SYSERR;
    116       }
    117     }
    118     gcresp->details.ok.num_charities = num_charity;
    119     gcresp->details.ok.charities = charities;
    120   }
    121   cgh->cb (cgh->cb_cls,
    122            gcresp);
    123   cgh->cb = NULL;
    124   GNUNET_free (charities);
    125   return GNUNET_OK;
    126 }
    127 
    128 
    129 /**
    130  * Callback used when downloading the reply to a /charities request
    131  * is complete.
    132  *
    133  * @param cls the `struct KeysRequest`
    134  * @param response_code HTTP response code, 0 on error
    135  * @param resp_obj parsed JSON result, NULL on error
    136  */
    137 static void
    138 handle_charities_get_finished (void *cls,
    139                                long response_code,
    140                                const void *resp_obj)
    141 {
    142   struct DONAU_CharitiesGetHandle *cgh = cls;
    143   const json_t *j = resp_obj;
    144   struct DONAU_GetCharitiesResponse gcresp = {
    145     .hr.reply = j,
    146     .hr.http_status = (unsigned int) response_code
    147   };
    148 
    149   cgh->job = NULL;
    150   switch (response_code)
    151   {
    152   case 0:
    153     gcresp.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    154     break;
    155   case MHD_HTTP_OK:
    156     if (GNUNET_OK !=
    157         handle_charities_get_ok (j,
    158                                  cgh,
    159                                  &gcresp))
    160     {
    161       gcresp.hr.http_status = 0;
    162       gcresp.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
    163     }
    164     break;
    165   case MHD_HTTP_BAD_REQUEST:
    166     /* This should never happen, either us or the donau is buggy
    167        (or API version conflict); just pass JSON reply to the application */
    168     gcresp.hr.ec = TALER_JSON_get_error_code (j);
    169     gcresp.hr.hint = TALER_JSON_get_error_hint (j);
    170     break;
    171   case MHD_HTTP_FORBIDDEN:
    172     /* Nothing really to verify */
    173     gcresp.hr.ec = TALER_JSON_get_error_code (j);
    174     gcresp.hr.hint = TALER_JSON_get_error_hint (j);
    175     break;
    176   case MHD_HTTP_NOT_FOUND:
    177     /* Nothing really to verify, this should never
    178        happen, we should pass the JSON reply to the application */
    179     gcresp.hr.ec = TALER_JSON_get_error_code (j);
    180     gcresp.hr.hint = TALER_JSON_get_error_hint (j);
    181     break;
    182   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    183     /* Server had an internal issue; we should retry, but this API
    184        leaves this to the application */
    185     gcresp.hr.ec = TALER_JSON_get_error_code (j);
    186     gcresp.hr.hint = TALER_JSON_get_error_hint (j);
    187     break;
    188   default:
    189     /* unexpected response code */
    190     GNUNET_break_op (0);
    191     gcresp.hr.ec = TALER_JSON_get_error_code (j);
    192     gcresp.hr.hint = TALER_JSON_get_error_hint (j);
    193     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    194                 "Unexpected response code %u/%d for GET %s\n",
    195                 (unsigned int) response_code,
    196                 (int) gcresp.hr.ec,
    197                 cgh->url);
    198     break;
    199   }
    200 
    201   if (NULL != cgh->cb)
    202   {
    203     cgh->cb (cgh->cb_cls,
    204              &gcresp);
    205     cgh->cb = NULL;
    206   }
    207   DONAU_charities_get_cancel (cgh);
    208 }
    209 
    210 
    211 struct DONAU_CharitiesGetHandle *
    212 DONAU_charities_get (
    213   struct GNUNET_CURL_Context *ctx,
    214   const char *url,
    215   const struct DONAU_BearerToken *bearer, // FIXME-#9435: check authorization
    216   DONAU_GetCharitiesResponseCallback cb,
    217   void *cb_cls)
    218 {
    219   struct DONAU_CharitiesGetHandle *cgh;
    220   CURL *eh;
    221   const char *arg_str = "charities";
    222 
    223   TALER_LOG_DEBUG ("Connecting to the donau (%s)\n",
    224                    url);
    225   cgh = GNUNET_new (struct DONAU_CharitiesGetHandle);
    226   cgh->url = GNUNET_strdup (url);
    227   cgh->cb = cb;
    228   cgh->cb_cls = cb_cls;
    229   cgh->url = TALER_url_join (url,
    230                              arg_str,
    231                              NULL);
    232   if (NULL == cgh->url)
    233   {
    234     GNUNET_free (cgh);
    235     return NULL;
    236   }
    237   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    238               "Requesting all charities with URL `%s'.\n",
    239               cgh->url);
    240   eh = DONAU_curl_easy_get_ (cgh->url);
    241   if (NULL == eh)
    242   {
    243     GNUNET_break (0);
    244     GNUNET_free (cgh->url);
    245     GNUNET_free (cgh);
    246     return NULL;
    247   }
    248   cgh->job = GNUNET_CURL_job_add_with_ct_json (ctx,
    249                                                eh,
    250                                                &handle_charities_get_finished,
    251                                                cgh);
    252   GNUNET_assert (NULL != cgh->job);
    253   if (NULL != bearer)
    254   {
    255     struct curl_slist *auth;
    256     char *hdr;
    257 
    258     GNUNET_asprintf (&hdr,
    259                      "%s: Bearer %s",
    260                      MHD_HTTP_HEADER_AUTHORIZATION,
    261                      bearer->token);
    262     auth = curl_slist_append (NULL,
    263                               hdr);
    264     GNUNET_free (hdr);
    265     GNUNET_CURL_extend_headers (cgh->job,
    266                                 auth);
    267     curl_slist_free_all (auth);
    268   }
    269   return cgh;
    270 }
    271 
    272 
    273 void
    274 DONAU_charities_get_cancel (
    275   struct DONAU_CharitiesGetHandle *cgh)
    276 {
    277   if (NULL != cgh->job)
    278   {
    279     GNUNET_CURL_job_cancel (cgh->job);
    280     cgh->job = NULL;
    281   }
    282   GNUNET_free (cgh->url);
    283   GNUNET_free (cgh);
    284 }