donau

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

donau_api_donation_statement_get.c (8509B)


      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_donation_statement_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 /donation-statement/$YEAR/$HASH_DONOR_ID request.
     34  */
     35 struct DONAU_DonationStatementGetHandle
     36 {
     37   /**
     38    * The url for the /donation-statement/$YEAR/$HASH_DONOR_ID 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_GetDonationStatmentResponseCallback cb;
     51 
     52   /**
     53    * Salted and hashed donor id
     54    */
     55   struct DONAU_HashDonorTaxId h_donor_tax_id;
     56 
     57   /**
     58    * year
     59    */
     60   uint64_t year;
     61 
     62   /**
     63    * Closure to pass to @e cb.
     64    */
     65   void *cb_cls;
     66 
     67 };
     68 
     69 
     70 /**
     71  * Callback used when downloading the reply to a /donation-statement/$YEAR/$HASH_DONOR_ID request
     72  * is complete.
     73  *
     74  * @param cls the `struct KeysRequest`
     75  * @param response_code HTTP response code, 0 on error
     76  * @param resp_obj parsed JSON result, NULL on error
     77  */
     78 static void
     79 handle_donation_statement_get_finished (void *cls,
     80                                         long response_code,
     81                                         const void *resp_obj)
     82 {
     83   struct DONAU_DonationStatementGetHandle *dsgh = cls;
     84   const json_t *j = resp_obj;
     85   struct DONAU_DonationStatementResponse dsresp = {
     86     .hr.reply = j,
     87     .hr.http_status = (unsigned int) response_code
     88   };
     89 
     90   dsgh->job = NULL;
     91   switch (response_code)
     92   {
     93   case 0:
     94     dsresp.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     95     break;
     96   case MHD_HTTP_OK:
     97     {
     98       struct GNUNET_JSON_Specification spec[] = {
     99         GNUNET_JSON_spec_fixed_auto (
    100           "donation_statement_sig",
    101           &dsresp.details.ok.donation_statement_sig),
    102         TALER_JSON_spec_amount_any ("total",
    103                                     &dsresp.details.ok.total_amount),
    104         GNUNET_JSON_spec_fixed_auto ("donau_pub",
    105                                      &dsresp.details.ok.donau_pub),
    106         GNUNET_JSON_spec_end ()
    107       };
    108 
    109       if (GNUNET_OK !=
    110           GNUNET_JSON_parse (j,
    111                              spec,
    112                              NULL,
    113                              NULL))
    114       {
    115         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    116                     "Could not parse response from donation-statement GET\n");
    117         GNUNET_break_op (0);
    118         dsresp.hr.http_status = 0;
    119         dsresp.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
    120       }
    121       else if (GNUNET_OK !=
    122                DONAU_donation_statement_verify (
    123                  &dsresp.details.ok.total_amount,
    124                  (uint32_t) dsgh->year,
    125                  &dsgh->h_donor_tax_id,
    126                  &dsresp.details.ok.donau_pub,
    127                  &dsresp.details.ok.donation_statement_sig))
    128       {
    129         /* The donau returned a total/signature that does not verify under
    130            the returned donau public key.  NOTE: the caller must still
    131            cross-check donau_pub against the trusted signing keys from
    132            /keys; this only rejects internally-inconsistent replies. */
    133         GNUNET_break_op (0);
    134         dsresp.hr.http_status = 0;
    135         dsresp.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
    136       }
    137       dsgh->cb (dsgh->cb_cls,
    138                 &dsresp);
    139       dsgh->cb = NULL;
    140       break;
    141     }
    142   case MHD_HTTP_BAD_REQUEST:
    143     /* This should never happen, either us or the donau is buggy
    144        (or API version conflict); just pass JSON reply to the application */
    145     dsresp.hr.ec = TALER_JSON_get_error_code (j);
    146     dsresp.hr.hint = TALER_JSON_get_error_hint (j);
    147     break;
    148   case MHD_HTTP_NOT_FOUND:
    149     /* Nothing really to verify, this should never
    150        happen, we should pass the JSON reply to the application */
    151     dsresp.hr.ec = TALER_JSON_get_error_code (j);
    152     dsresp.hr.hint = TALER_JSON_get_error_hint (j);
    153     break;
    154   case MHD_HTTP_NO_CONTENT:
    155     /* No donation statement exists for the given year and donor;
    156        this is a normal, non-error outcome (empty body). */
    157     break;
    158   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    159     /* Server had an internal issue; we should retry, but this API
    160        leaves this to the application */
    161     dsresp.hr.ec = TALER_JSON_get_error_code (j);
    162     dsresp.hr.hint = TALER_JSON_get_error_hint (j);
    163     break;
    164   case MHD_HTTP_SERVICE_UNAVAILABLE:
    165     /* The donau is lacking the keys to create the donation statement. */
    166     dsresp.hr.ec = TALER_JSON_get_error_code (j);
    167     dsresp.hr.hint = TALER_JSON_get_error_hint (j);
    168     break;
    169   default:
    170     /* unexpected response code */
    171     GNUNET_break_op (0);
    172     dsresp.hr.ec = TALER_JSON_get_error_code (j);
    173     dsresp.hr.hint = TALER_JSON_get_error_hint (j);
    174     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    175                 "Unexpected response code %u/%d for GET %s\n",
    176                 (unsigned int) response_code,
    177                 (int) dsresp.hr.ec,
    178                 dsgh->url);
    179     break;
    180   }
    181   if (NULL != dsgh->cb)
    182   {
    183     dsgh->cb (dsgh->cb_cls,
    184               &dsresp);
    185     dsgh->cb = NULL;
    186   }
    187   DONAU_donation_statement_get_cancel (dsgh);
    188 }
    189 
    190 
    191 /**
    192  * Prepares the request URL for the donation-statement-get request
    193  *
    194  * @param dsgh The handler
    195  * @param donau_url The base-URL to the donau
    196  */
    197 static
    198 enum GNUNET_GenericReturnValue
    199 prepare_url (
    200   struct DONAU_DonationStatementGetHandle *dsgh,
    201   const char *donau_url)
    202 {
    203   char arg_str[sizeof (struct DONAU_HashDonorTaxId) * 2 + 32];
    204   char donor_id_hash_str[sizeof (struct DONAU_HashDonorTaxId) * 2];
    205   char *end;
    206 
    207   end = GNUNET_STRINGS_data_to_string (
    208     &dsgh->h_donor_tax_id,
    209     sizeof (struct DONAU_HashDonorTaxId),
    210     donor_id_hash_str,
    211     sizeof (donor_id_hash_str));
    212   *end = '\0';
    213   GNUNET_snprintf (arg_str,
    214                    sizeof (arg_str),
    215                    "donation-statement/%llu/%s",
    216                    (unsigned long long) dsgh->year,
    217                    donor_id_hash_str);
    218 
    219   dsgh->url = TALER_url_join (donau_url,
    220                               arg_str,
    221                               NULL);
    222   if (NULL == dsgh->url)
    223   {
    224     GNUNET_break (0);
    225     DONAU_donation_statement_get_cancel (dsgh);
    226     return GNUNET_SYSERR;
    227   }
    228 
    229   return GNUNET_OK;
    230 }
    231 
    232 
    233 struct DONAU_DonationStatementGetHandle *
    234 DONAU_donation_statement_get (
    235   struct GNUNET_CURL_Context *ctx,
    236   const char *url,
    237   const uint64_t year,
    238   const struct DONAU_HashDonorTaxId *h_donor_tax_id,
    239   DONAU_GetDonationStatmentResponseCallback cb,
    240   void *cb_cls)
    241 {
    242   struct DONAU_DonationStatementGetHandle *dsgh;
    243   CURL *eh;
    244 
    245   TALER_LOG_DEBUG ("Connecting to the donau (%s)\n",
    246                    url);
    247 
    248   dsgh = GNUNET_new (struct DONAU_DonationStatementGetHandle);
    249   dsgh->cb = cb;
    250   dsgh->cb_cls = cb_cls;
    251   dsgh->year = year;
    252   dsgh->h_donor_tax_id = *h_donor_tax_id;
    253   if (GNUNET_OK != prepare_url (dsgh,
    254                                 url))
    255     return NULL;
    256   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    257               "Requesting a charity with URL `%s'.\n",
    258               dsgh->url);
    259   eh = DONAU_curl_easy_get_ (dsgh->url);
    260   if (NULL == eh)
    261   {
    262     GNUNET_break (0);
    263     GNUNET_free (dsgh->url);
    264     GNUNET_free (dsgh);
    265     return NULL;
    266   }
    267   dsgh->job = GNUNET_CURL_job_add_with_ct_json (ctx,
    268                                                 eh,
    269                                                 &
    270                                                 handle_donation_statement_get_finished,
    271                                                 dsgh);
    272   return dsgh;
    273 }
    274 
    275 
    276 void
    277 DONAU_donation_statement_get_cancel (
    278   struct DONAU_DonationStatementGetHandle *dsgh)
    279 {
    280   if (NULL != dsgh->job)
    281   {
    282     GNUNET_CURL_job_cancel (dsgh->job);
    283     dsgh->job = NULL;
    284   }
    285   GNUNET_free (dsgh->url);
    286   GNUNET_free (dsgh);
    287 }