Line data Source code
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 0 : 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 0 : bool amount_missing = true;
60 : struct GNUNET_JSON_Specification spec[] = {
61 0 : GNUNET_JSON_spec_mark_optional (
62 : TALER_JSON_spec_amount ("amount",
63 0 : h->currency,
64 : &amount),
65 : &amount_missing),
66 0 : GNUNET_JSON_spec_end ()
67 : };
68 : enum GNUNET_GenericReturnValue ret;
69 :
70 0 : if ( (NULL != body) &&
71 : (GNUNET_OK !=
72 0 : (ret = TALER_MHD_parse_json_data (connection,
73 : body,
74 : spec))) )
75 : {
76 0 : GNUNET_break_op (0);
77 0 : return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
78 : }
79 :
80 0 : GNUNET_assert (0 ==
81 : pthread_mutex_lock (&h->big_lock));
82 0 : acc = TALER_FAKEBANK_lookup_account_ (h,
83 : account,
84 : NULL);
85 0 : if (NULL == acc)
86 : {
87 0 : GNUNET_assert (0 ==
88 : pthread_mutex_unlock (&h->big_lock));
89 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
90 : "Account %s is unknown\n",
91 : account);
92 0 : return TALER_MHD_reply_with_error (connection,
93 : MHD_HTTP_NOT_FOUND,
94 : TALER_EC_BANK_UNKNOWN_ACCOUNT,
95 : account);
96 : }
97 0 : wo = TALER_FAKEBANK_lookup_withdrawal_operation_ (h,
98 : withdrawal_id);
99 0 : if ( (NULL == wo) ||
100 0 : (acc != wo->debit_account) )
101 : {
102 0 : GNUNET_assert (0 ==
103 : pthread_mutex_unlock (&h->big_lock));
104 0 : return TALER_MHD_reply_with_error (connection,
105 : MHD_HTTP_NOT_FOUND,
106 : TALER_EC_BANK_TRANSACTION_NOT_FOUND,
107 : withdrawal_id);
108 : }
109 0 : if (NULL == wo->exchange_account)
110 : {
111 0 : GNUNET_assert (0 ==
112 : pthread_mutex_unlock (&h->big_lock));
113 0 : return TALER_MHD_reply_with_error (connection,
114 : MHD_HTTP_BAD_REQUEST,
115 : TALER_EC_BANK_POST_WITHDRAWAL_OPERATION_REQUIRED,
116 : NULL);
117 : }
118 0 : if ( (NULL != wo->amount) &&
119 0 : (! amount_missing) &&
120 0 : (0 != TALER_amount_cmp (&amount,
121 0 : wo->amount)) )
122 : {
123 0 : GNUNET_assert (0 ==
124 : pthread_mutex_unlock (&h->big_lock));
125 0 : return TALER_MHD_reply_with_error (connection,
126 : MHD_HTTP_CONFLICT,
127 : TALER_EC_BANK_CONFIRM_ABORT_CONFLICT,
128 : "amount inconsistent");
129 : }
130 0 : if ( (NULL == wo->amount) &&
131 : (amount_missing) )
132 : {
133 0 : GNUNET_assert (0 ==
134 : pthread_mutex_unlock (&h->big_lock));
135 0 : return TALER_MHD_reply_with_error (connection,
136 : MHD_HTTP_CONFLICT,
137 : TALER_EC_BANK_CONFIRM_ABORT_CONFLICT,
138 : "amount required");
139 : }
140 0 : if (NULL == wo->amount)
141 : {
142 0 : GNUNET_assert (! amount_missing);
143 0 : wo->amount = GNUNET_new (struct TALER_Amount);
144 0 : *wo->amount = amount;
145 : }
146 0 : if (wo->aborted)
147 : {
148 0 : GNUNET_assert (0 ==
149 : pthread_mutex_unlock (&h->big_lock));
150 0 : return TALER_MHD_reply_with_error (connection,
151 : MHD_HTTP_CONFLICT,
152 : TALER_EC_BANK_CONFIRM_ABORT_CONFLICT,
153 : withdrawal_id);
154 : }
155 0 : GNUNET_assert (0 ==
156 : pthread_mutex_unlock (&h->big_lock));
157 0 : if (GNUNET_OK !=
158 0 : TALER_FAKEBANK_make_admin_transfer_ (
159 : h,
160 0 : wo->debit_account->account_name,
161 0 : wo->exchange_account->account_name,
162 0 : wo->amount,
163 0 : &wo->reserve_pub,
164 : &wo->row_id,
165 : &wo->timestamp))
166 : {
167 0 : 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 0 : GNUNET_assert (0 ==
177 : pthread_mutex_lock (&h->big_lock));
178 0 : wo->confirmation_done = true;
179 0 : TALER_FAKEBANK_notify_withdrawal_ (h,
180 : wo);
181 0 : GNUNET_assert (0 ==
182 : pthread_mutex_unlock (&h->big_lock));
183 0 : 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 0 : 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 0 : GNUNET_assert (0 ==
213 : pthread_mutex_lock (&h->big_lock));
214 0 : acc = TALER_FAKEBANK_lookup_account_ (h,
215 : account,
216 : NULL);
217 0 : if (NULL == acc)
218 : {
219 0 : GNUNET_assert (0 ==
220 : pthread_mutex_unlock (&h->big_lock));
221 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
222 : "Account %s is unknown\n",
223 : account);
224 0 : return TALER_MHD_reply_with_error (connection,
225 : MHD_HTTP_NOT_FOUND,
226 : TALER_EC_BANK_UNKNOWN_ACCOUNT,
227 : account);
228 : }
229 0 : wo = TALER_FAKEBANK_lookup_withdrawal_operation_ (h,
230 : withdrawal_id);
231 0 : if ( (NULL == wo) ||
232 0 : (acc != wo->debit_account) )
233 : {
234 0 : GNUNET_assert (0 ==
235 : pthread_mutex_unlock (&h->big_lock));
236 0 : return TALER_MHD_reply_with_error (connection,
237 : MHD_HTTP_NOT_FOUND,
238 : TALER_EC_BANK_TRANSACTION_NOT_FOUND,
239 : withdrawal_id);
240 : }
241 0 : if (wo->confirmation_done)
242 : {
243 0 : GNUNET_assert (0 ==
244 : pthread_mutex_unlock (&h->big_lock));
245 0 : return TALER_MHD_reply_with_error (connection,
246 : MHD_HTTP_CONFLICT,
247 : TALER_EC_BANK_ABORT_CONFIRM_CONFLICT,
248 : withdrawal_id);
249 : }
250 0 : wo->aborted = true;
251 0 : TALER_FAKEBANK_notify_withdrawal_ (h,
252 : wo);
253 0 : GNUNET_assert (0 ==
254 : pthread_mutex_unlock (&h->big_lock));
255 0 : return TALER_MHD_reply_static (connection,
256 : MHD_HTTP_NO_CONTENT,
257 : NULL,
258 : NULL,
259 : 0);
260 : }
261 :
262 :
263 : MHD_RESULT
264 0 : 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 0 : struct ConnectionContext *cc = *con_cls;
275 0 : json_t *json = NULL;
276 :
277 0 : if (NULL == cc)
278 : {
279 0 : cc = GNUNET_new (struct ConnectionContext);
280 0 : cc->ctx_cleaner = &GNUNET_MHD_post_parser_cleanup;
281 0 : *con_cls = cc;
282 : }
283 0 : if (0 != *upload_data_size)
284 : {
285 : enum GNUNET_MHD_PostResult pr;
286 :
287 0 : pr = GNUNET_MHD_post_parser (REQUEST_BUFFER_MAX,
288 : connection,
289 : &cc->ctx,
290 : upload_data,
291 : upload_data_size,
292 : &json);
293 0 : switch (pr)
294 : {
295 0 : case GNUNET_MHD_PR_OUT_OF_MEMORY:
296 0 : GNUNET_break (0);
297 0 : return MHD_NO;
298 0 : case GNUNET_MHD_PR_CONTINUE:
299 0 : return MHD_YES;
300 0 : case GNUNET_MHD_PR_REQUEST_TOO_LARGE:
301 0 : GNUNET_break (0);
302 0 : return MHD_NO;
303 0 : case GNUNET_MHD_PR_JSON_INVALID:
304 0 : GNUNET_break (0);
305 0 : return MHD_NO;
306 0 : case GNUNET_MHD_PR_SUCCESS:
307 0 : break;
308 : }
309 : }
310 :
311 0 : if (0 == strcmp (op,
312 : "/confirm"))
313 : {
314 : MHD_RESULT res;
315 :
316 0 : res = bank_withdrawals_confirm (h,
317 : connection,
318 : account,
319 : withdrawal_id,
320 : json);
321 0 : json_decref (json);
322 0 : return res;
323 : }
324 0 : if (0 == strcmp (op,
325 : "/abort"))
326 : {
327 : MHD_RESULT res;
328 :
329 0 : res = bank_withdrawals_abort (h,
330 : connection,
331 : account,
332 : withdrawal_id,
333 : json);
334 0 : json_decref (json);
335 0 : return res;
336 : }
337 0 : GNUNET_break_op (0);
338 0 : json_decref (json);
339 0 : return TALER_MHD_reply_with_error (connection,
340 : MHD_HTTP_NOT_FOUND,
341 : TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
342 : op);
343 : }
|