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