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