donau

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

donau-httpd_batch-submit.c (7760B)


      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 under the
      6   terms of the GNU Affero General Public License as published by the Free Software
      7   Foundation; either version 3, 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 Affero General Public License for more details.
     12 
     13   You should have received a copy of the GNU Affero General Public License along with
     14   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file donau-httpd_batch-submit.c
     18  * @brief Handle request to insert a submitted receipt.
     19  * @author Johannes Casaburi
     20  */
     21 #include "donau_config.h"
     22 #include <gnunet/gnunet_util_lib.h>
     23 #include <gnunet/gnunet_json_lib.h>
     24 #include <jansson.h>
     25 #include <microhttpd.h>
     26 #include <pthread.h>
     27 #include "taler/taler_json_lib.h"
     28 #include "taler/taler_mhd_lib.h"
     29 #include "taler/taler_signatures.h"
     30 #include "donaudb_plugin.h"
     31 #include "donau-httpd_batch-submit.h"
     32 #include "donau-httpd_keys.h"
     33 
     34 
     35 /**
     36  * Closure for #insert_submitted_receipts()
     37  */
     38 struct InsertReceiptContext
     39 {
     40   struct DONAU_HashDonorTaxId h_donor_tax_id;
     41   struct DONAU_DonationReceipt *donation_receipts;
     42   size_t num_dr;
     43   uint64_t donation_year;
     44 };
     45 
     46 /**
     47  * Parse a donation receipt encoded in JSON.
     48  *
     49  * @param[out] dr where to return the result
     50  * @param dr_obj json to parse
     51  * @return #GNUNET_OK if all is fine, #GNUNET_SYSERR if @a dr_obj
     52  * is malformed.
     53  */
     54 static enum GNUNET_GenericReturnValue
     55 parse_json_dr (struct DONAU_DonationReceipt *dr,
     56                const json_t *dr_obj)
     57 {
     58   struct GNUNET_JSON_Specification spec[] = {
     59     GNUNET_JSON_spec_fixed_auto ("h_donation_unit_pub",
     60                                  &dr->h_donation_unit_pub),
     61     GNUNET_JSON_spec_fixed_auto ("nonce",
     62                                  &dr->nonce),
     63     GNUNET_JSON_spec_unblinded_signature ("donation_unit_sig",
     64                                           &dr->donation_unit_sig.unblinded_sig),
     65     GNUNET_JSON_spec_end ()
     66   };
     67 
     68   if (GNUNET_OK !=
     69       GNUNET_JSON_parse (dr_obj,
     70                          spec,
     71                          NULL,
     72                          NULL))
     73   {
     74     GNUNET_break_op (0);
     75     return GNUNET_SYSERR;
     76   }
     77 
     78   return GNUNET_OK;
     79 }
     80 
     81 
     82 /**
     83  * Free data in @a irc, but not @a irc itself
     84  *
     85  * @param[in,out] irc data structure to clean up
     86  */
     87 static void
     88 free_irc (struct InsertReceiptContext *irc)
     89 {
     90   for (size_t i = 0; i<irc->num_dr; i++)
     91     GNUNET_CRYPTO_unblinded_sig_decref (irc->donation_receipts[i].
     92                                         donation_unit_sig.unblinded_sig);
     93   GNUNET_free (irc->donation_receipts);
     94 }
     95 
     96 
     97 MHD_RESULT
     98 DH_handler_submit_receipts_post (struct DH_RequestContext *rc,
     99                                  const json_t *root,
    100                                  const char *const args[])
    101 {
    102   struct InsertReceiptContext irc = {0};
    103   const json_t *donation_receipts;
    104   struct GNUNET_JSON_Specification spec[] = {
    105     GNUNET_JSON_spec_fixed_auto ("h_donor_tax_id",
    106                                  &irc.h_donor_tax_id),
    107     GNUNET_JSON_spec_array_const ("donation_receipts",
    108                                   &donation_receipts),
    109     GNUNET_JSON_spec_uint64 ("donation_year",
    110                              &irc.donation_year),
    111     GNUNET_JSON_spec_end ()
    112   };
    113   size_t num_dr;
    114 
    115   (void) args;
    116   {
    117     enum GNUNET_GenericReturnValue res;
    118 
    119     res = TALER_MHD_parse_json_data (rc->connection,
    120                                      root,
    121                                      spec);
    122     if (GNUNET_SYSERR == res)
    123       return MHD_NO; /* hard failure */
    124     if (GNUNET_NO == res)
    125     {
    126       GNUNET_break_op (0);
    127       return MHD_YES; /* failure */
    128     }
    129   }
    130 
    131   /* parse the donation receipts */
    132   num_dr = json_array_size (donation_receipts);
    133 
    134   if (0 == num_dr)
    135   {
    136     GNUNET_break_op (0);
    137     return TALER_MHD_reply_with_error (rc->connection,
    138                                        MHD_HTTP_BAD_REQUEST,
    139                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
    140                                        "donation_receipts");
    141   }
    142   {
    143     json_t *dr_obj;
    144     size_t index;
    145 
    146     irc.num_dr = num_dr;
    147     irc.donation_receipts = GNUNET_new_array (num_dr,
    148                                               struct DONAU_DonationReceipt);
    149 
    150     json_array_foreach (donation_receipts,
    151                         index,
    152                         dr_obj)
    153     {
    154       if (GNUNET_SYSERR ==
    155           parse_json_dr (&irc.donation_receipts[index],
    156                          dr_obj))
    157       {
    158         GNUNET_break_op (0);
    159         free_irc (&irc);
    160         return TALER_MHD_reply_with_error (rc->connection,
    161                                            MHD_HTTP_BAD_REQUEST,
    162                                            TALER_EC_GENERIC_PARAMETER_MALFORMED,
    163                                            "donation_receipts");
    164       }
    165     }
    166   }
    167 
    168   for (size_t i = 0; i < num_dr; i++)
    169   {
    170     struct DONAU_UniqueDonorIdentifierHashP udi_hash;
    171     struct DH_DonationUnitKey *dk;
    172 
    173     /* Check nonce unique*/
    174     for (size_t j = i + 1; j < num_dr; j++)
    175     {
    176       if (0 ==
    177           GNUNET_memcmp (&irc.donation_receipts[i].nonce,
    178                          &irc.donation_receipts[j].nonce))
    179       {
    180         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    181                     "Donation receipt nonce is not unique!\n");
    182         free_irc (&irc);
    183         return TALER_MHD_reply_with_error (rc->connection,
    184                                            MHD_HTTP_CONFLICT,
    185                                            TALER_EC_DONAU_DONOR_IDENTIFIER_NONCE_REUSE,
    186                                            NULL);
    187       }
    188     }
    189 
    190     /* Check if donation unit exists*/
    191     if (NULL == (dk = DH_keys_donation_unit_by_hash (
    192                    &irc.donation_receipts[i].h_donation_unit_pub)))
    193     {
    194       GNUNET_break_op (0);
    195       free_irc (&irc);
    196       return TALER_MHD_reply_with_error (rc->connection,
    197                                          MHD_HTTP_NOT_FOUND,
    198                                          TALER_EC_DONAU_GENERIC_DONATION_UNIT_UNKNOWN,
    199                                          NULL);
    200     }
    201 
    202     DONAU_unique_donor_id_hash (
    203       &irc.h_donor_tax_id,
    204       &irc.donation_receipts[i].nonce,
    205       &udi_hash);
    206 
    207     /* Check signature*/
    208     if (GNUNET_OK != DONAU_donation_receipt_verify (
    209           &dk->donation_unit_pub,
    210           &udi_hash,
    211           &irc.donation_receipts[i].donation_unit_sig))
    212     {
    213       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    214                   "Donation receipt signature invalid!\n");
    215       free_irc (&irc);
    216       return TALER_MHD_reply_with_error (rc->connection,
    217                                          MHD_HTTP_FORBIDDEN,
    218                                          TALER_EC_DONAU_DONATION_RECEIPT_SIGNATURE_INVALID,
    219                                          NULL);
    220 
    221     }
    222   }
    223 
    224   {
    225     enum GNUNET_DB_QueryStatus qs;
    226 
    227     qs = DH_plugin->insert_submitted_receipts (
    228       DH_plugin->cls,
    229       &irc.h_donor_tax_id,
    230       num_dr,
    231       irc.donation_receipts,
    232       irc.donation_year);
    233     free_irc (&irc);
    234     if (qs < 0)
    235     {
    236       GNUNET_break (0);
    237       return TALER_MHD_reply_with_error (rc->connection,
    238                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
    239                                          TALER_EC_GENERIC_DB_FETCH_FAILED,
    240                                          NULL);
    241     }
    242   }
    243   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    244               "submitted receipts inserted!\n");
    245 
    246   return TALER_MHD_reply_static (
    247     rc->connection,
    248     MHD_HTTP_CREATED,
    249     NULL,
    250     NULL,
    251     0);
    252 }
    253 
    254 
    255 /* end of donau-httpd_batch-submit.c */