fakebank_bank_post_withdrawals_id_op.c (11415B)
1 /* 2 This file is part of TALER 3 (C) 2016-2024 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or 6 modify it under the terms of the GNU General Public License 7 as published by the Free Software Foundation; either version 3, 8 or (at your option) any later version. 9 10 TALER is distributed in the hope that it will be useful, 11 but 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, 17 see <http://www.gnu.org/licenses/> 18 */ 19 /** 20 * @file bank-lib/fakebank_bank_post_withdrawals_id_op.c 21 * @brief implement bank API POST /accounts/$ACCOUNT/withdrawals/$WID/$OP endpoint(s) 22 * @author Christian Grothoff <christian@grothoff.org> 23 */ 24 #include <pthread.h> 25 #include "taler/taler_fakebank_lib.h" 26 #include "taler/taler_bank_service.h" 27 #include "taler/taler_mhd_lib.h" 28 #include <gnunet/gnunet_mhd_compat.h> 29 #include <gnunet/gnunet_mhd_lib.h> 30 #include "fakebank.h" 31 #include "fakebank_bank_post_withdrawals_id_op.h" 32 #include "fakebank_common_lookup.h" 33 #include "fakebank_common_lp.h" 34 #include "fakebank_common_make_admin_transfer.h" 35 36 37 /** 38 * Handle POST /accounts/$ACC/withdrawals/{withdrawal_id}/confirm request. 39 * 40 * @param h our fakebank handle 41 * @param connection the connection 42 * @param account name of the account 43 * @param withdrawal_id the withdrawal operation identifier 44 * @param body uploaded JSON body, NULL if none 45 * @return MHD result code 46 */ 47 static MHD_RESULT 48 bank_withdrawals_confirm ( 49 struct TALER_FAKEBANK_Handle *h, 50 struct MHD_Connection *connection, 51 const char *account, 52 const char *withdrawal_id, 53 const json_t *body) 54 { 55 const struct Account *acc; 56 struct WithdrawalOperation *wo; 57 struct TALER_Amount amount; 58 bool amount_missing = true; 59 struct GNUNET_JSON_Specification spec[] = { 60 GNUNET_JSON_spec_mark_optional ( 61 TALER_JSON_spec_amount ("amount", 62 h->currency, 63 &amount), 64 &amount_missing), 65 GNUNET_JSON_spec_end () 66 }; 67 enum GNUNET_GenericReturnValue ret; 68 69 if ( (NULL != body) && 70 (GNUNET_OK != 71 (ret = TALER_MHD_parse_json_data (connection, 72 body, 73 spec))) ) 74 { 75 GNUNET_break_op (0); 76 return (GNUNET_NO == ret) ? MHD_YES : MHD_NO; 77 } 78 79 GNUNET_assert (0 == 80 pthread_mutex_lock (&h->big_lock)); 81 acc = TALER_FAKEBANK_lookup_account_ (h, 82 account, 83 NULL); 84 if (NULL == acc) 85 { 86 GNUNET_assert (0 == 87 pthread_mutex_unlock (&h->big_lock)); 88 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 89 "Account %s is unknown\n", 90 account); 91 return TALER_MHD_reply_with_error (connection, 92 MHD_HTTP_NOT_FOUND, 93 TALER_EC_BANK_UNKNOWN_ACCOUNT, 94 account); 95 } 96 wo = TALER_FAKEBANK_lookup_withdrawal_operation_ (h, 97 withdrawal_id); 98 if ( (NULL == wo) || 99 (acc != wo->debit_account) ) 100 { 101 GNUNET_assert (0 == 102 pthread_mutex_unlock (&h->big_lock)); 103 return TALER_MHD_reply_with_error (connection, 104 MHD_HTTP_NOT_FOUND, 105 TALER_EC_BANK_TRANSACTION_NOT_FOUND, 106 withdrawal_id); 107 } 108 if (NULL == wo->exchange_account) 109 { 110 GNUNET_assert (0 == 111 pthread_mutex_unlock (&h->big_lock)); 112 return TALER_MHD_reply_with_error (connection, 113 MHD_HTTP_BAD_REQUEST, 114 TALER_EC_BANK_POST_WITHDRAWAL_OPERATION_REQUIRED, 115 NULL); 116 } 117 if ( (NULL != wo->amount) && 118 (! amount_missing) && 119 (0 != TALER_amount_cmp (&amount, 120 wo->amount)) ) 121 { 122 GNUNET_assert (0 == 123 pthread_mutex_unlock (&h->big_lock)); 124 return TALER_MHD_reply_with_error (connection, 125 MHD_HTTP_CONFLICT, 126 TALER_EC_BANK_CONFIRM_ABORT_CONFLICT, 127 "amount inconsistent"); 128 } 129 if ( (NULL == wo->amount) && 130 (amount_missing) ) 131 { 132 GNUNET_assert (0 == 133 pthread_mutex_unlock (&h->big_lock)); 134 return TALER_MHD_reply_with_error (connection, 135 MHD_HTTP_CONFLICT, 136 TALER_EC_BANK_CONFIRM_ABORT_CONFLICT, 137 "amount required"); 138 } 139 if (NULL == wo->amount) 140 { 141 GNUNET_assert (! amount_missing); 142 wo->amount = GNUNET_new (struct TALER_Amount); 143 *wo->amount = amount; 144 } 145 if (wo->aborted) 146 { 147 GNUNET_assert (0 == 148 pthread_mutex_unlock (&h->big_lock)); 149 return TALER_MHD_reply_with_error (connection, 150 MHD_HTTP_CONFLICT, 151 TALER_EC_BANK_CONFIRM_ABORT_CONFLICT, 152 withdrawal_id); 153 } 154 GNUNET_assert (0 == 155 pthread_mutex_unlock (&h->big_lock)); 156 if (GNUNET_OK != 157 TALER_FAKEBANK_make_admin_transfer_ ( 158 h, 159 wo->debit_account->account_name, 160 wo->exchange_account->account_name, 161 wo->amount, 162 &wo->reserve_pub, 163 &wo->row_id, 164 &wo->timestamp)) 165 { 166 return TALER_MHD_reply_with_error (connection, 167 MHD_HTTP_CONFLICT, 168 TALER_EC_BANK_DUPLICATE_RESERVE_PUB_SUBJECT, 169 NULL); 170 } 171 /* Re-acquiring the lock and continuing to operate on 'wo' 172 is currently (!) acceptable because we NEVER free 'wo' 173 until shutdown. We may want to revise this if keeping 174 all withdraw operations in RAM becomes an issue... */ 175 GNUNET_assert (0 == 176 pthread_mutex_lock (&h->big_lock)); 177 wo->confirmation_done = true; 178 TALER_FAKEBANK_notify_withdrawal_ (h, 179 wo); 180 GNUNET_assert (0 == 181 pthread_mutex_unlock (&h->big_lock)); 182 return TALER_MHD_reply_static (connection, 183 MHD_HTTP_NO_CONTENT, 184 NULL, 185 NULL, 186 0); 187 } 188 189 190 /** 191 * Handle POST /accounts/$ACC/withdrawals/{withdrawal_id}/abort request. 192 * 193 * @param h our fakebank handle 194 * @param connection the connection 195 * @param account name of the account 196 * @param withdrawal_id the withdrawal operation identifier 197 * @param body uploaded JSON body, NULL if none 198 * @return MHD result code 199 */ 200 static MHD_RESULT 201 bank_withdrawals_abort ( 202 struct TALER_FAKEBANK_Handle *h, 203 struct MHD_Connection *connection, 204 const char *account, 205 const char *withdrawal_id, 206 const json_t *body) 207 { 208 struct WithdrawalOperation *wo; 209 const struct Account *acc; 210 211 GNUNET_assert (0 == 212 pthread_mutex_lock (&h->big_lock)); 213 acc = TALER_FAKEBANK_lookup_account_ (h, 214 account, 215 NULL); 216 if (NULL == acc) 217 { 218 GNUNET_assert (0 == 219 pthread_mutex_unlock (&h->big_lock)); 220 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 221 "Account %s is unknown\n", 222 account); 223 return TALER_MHD_reply_with_error (connection, 224 MHD_HTTP_NOT_FOUND, 225 TALER_EC_BANK_UNKNOWN_ACCOUNT, 226 account); 227 } 228 wo = TALER_FAKEBANK_lookup_withdrawal_operation_ (h, 229 withdrawal_id); 230 if ( (NULL == wo) || 231 (acc != wo->debit_account) ) 232 { 233 GNUNET_assert (0 == 234 pthread_mutex_unlock (&h->big_lock)); 235 return TALER_MHD_reply_with_error (connection, 236 MHD_HTTP_NOT_FOUND, 237 TALER_EC_BANK_TRANSACTION_NOT_FOUND, 238 withdrawal_id); 239 } 240 if (wo->confirmation_done) 241 { 242 GNUNET_assert (0 == 243 pthread_mutex_unlock (&h->big_lock)); 244 return TALER_MHD_reply_with_error (connection, 245 MHD_HTTP_CONFLICT, 246 TALER_EC_BANK_ABORT_CONFIRM_CONFLICT, 247 withdrawal_id); 248 } 249 wo->aborted = true; 250 TALER_FAKEBANK_notify_withdrawal_ (h, 251 wo); 252 GNUNET_assert (0 == 253 pthread_mutex_unlock (&h->big_lock)); 254 return TALER_MHD_reply_static (connection, 255 MHD_HTTP_NO_CONTENT, 256 NULL, 257 NULL, 258 0); 259 } 260 261 262 MHD_RESULT 263 TALER_FAKEBANK_bank_withdrawals_id_op_ ( 264 struct TALER_FAKEBANK_Handle *h, 265 struct MHD_Connection *connection, 266 const char *account, 267 const char *withdrawal_id, 268 const char *op, 269 const char *upload_data, 270 size_t *upload_data_size, 271 void **con_cls) 272 { 273 struct ConnectionContext *cc = *con_cls; 274 json_t *json = NULL; 275 276 if (NULL == cc) 277 { 278 cc = GNUNET_new (struct ConnectionContext); 279 cc->ctx_cleaner = &GNUNET_MHD_post_parser_cleanup; 280 *con_cls = cc; 281 } 282 if (0 != *upload_data_size) 283 { 284 enum GNUNET_MHD_PostResult pr; 285 286 pr = GNUNET_MHD_post_parser (REQUEST_BUFFER_MAX, 287 connection, 288 &cc->ctx, 289 upload_data, 290 upload_data_size, 291 &json); 292 switch (pr) 293 { 294 case GNUNET_MHD_PR_OUT_OF_MEMORY: 295 GNUNET_break (0); 296 return MHD_NO; 297 case GNUNET_MHD_PR_CONTINUE: 298 return MHD_YES; 299 case GNUNET_MHD_PR_REQUEST_TOO_LARGE: 300 GNUNET_break (0); 301 return MHD_NO; 302 case GNUNET_MHD_PR_JSON_INVALID: 303 GNUNET_break (0); 304 return MHD_NO; 305 case GNUNET_MHD_PR_SUCCESS: 306 break; 307 } 308 } 309 310 if (0 == strcmp (op, 311 "/confirm")) 312 { 313 MHD_RESULT res; 314 315 res = bank_withdrawals_confirm (h, 316 connection, 317 account, 318 withdrawal_id, 319 json); 320 json_decref (json); 321 return res; 322 } 323 if (0 == strcmp (op, 324 "/abort")) 325 { 326 MHD_RESULT res; 327 328 res = bank_withdrawals_abort (h, 329 connection, 330 account, 331 withdrawal_id, 332 json); 333 json_decref (json); 334 return res; 335 } 336 GNUNET_break_op (0); 337 json_decref (json); 338 return TALER_MHD_reply_with_error (connection, 339 MHD_HTTP_NOT_FOUND, 340 TALER_EC_GENERIC_ENDPOINT_UNKNOWN, 341 op); 342 }