Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2022 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 taler-exchange-httpd_purses_merge.c
18 : * @brief Handle /purses/$PID/merge requests; parses the POST and JSON and
19 : * verifies the reserve signature before handing things off
20 : * to the database.
21 : * @author Christian Grothoff
22 : */
23 : #include "platform.h"
24 : #include <gnunet/gnunet_util_lib.h>
25 : #include <gnunet/gnunet_json_lib.h>
26 : #include <jansson.h>
27 : #include <microhttpd.h>
28 : #include <pthread.h>
29 : #include "taler_dbevents.h"
30 : #include "taler_json_lib.h"
31 : #include "taler_kyclogic_lib.h"
32 : #include "taler_mhd_lib.h"
33 : #include "taler-exchange-httpd_purses_merge.h"
34 : #include "taler-exchange-httpd_responses.h"
35 : #include "taler_exchangedb_lib.h"
36 : #include "taler-exchange-httpd_keys.h"
37 : #include "taler-exchange-httpd_wire.h"
38 :
39 :
40 : /**
41 : * Closure for #merge_transaction.
42 : */
43 : struct PurseMergeContext
44 : {
45 : /**
46 : * Public key of the purse we are creating.
47 : */
48 : const struct TALER_PurseContractPublicKeyP *purse_pub;
49 :
50 : /**
51 : * Total amount to be put into the purse.
52 : */
53 : struct TALER_Amount target_amount;
54 :
55 : /**
56 : * Current amount in the purse.
57 : */
58 : struct TALER_Amount balance;
59 :
60 : /**
61 : * When should the purse expire.
62 : */
63 : struct GNUNET_TIME_Timestamp purse_expiration;
64 :
65 : /**
66 : * When the client signed the merge.
67 : */
68 : struct GNUNET_TIME_Timestamp merge_timestamp;
69 :
70 : /**
71 : * Our current time.
72 : */
73 : struct GNUNET_TIME_Timestamp exchange_timestamp;
74 :
75 : /**
76 : * Merge key for the purse.
77 : */
78 : struct TALER_PurseMergePublicKeyP merge_pub;
79 :
80 : /**
81 : * Signature of the reservce affiming this request.
82 : */
83 : struct TALER_ReserveSignatureP reserve_sig;
84 :
85 : /**
86 : * Signature of the client affiming the merge.
87 : */
88 : struct TALER_PurseMergeSignatureP merge_sig;
89 :
90 : /**
91 : * Public key of the reserve, as extracted from @e payto_uri.
92 : */
93 : struct TALER_ReservePublicKeyP reserve_pub;
94 :
95 : /**
96 : * Hash of the contract terms of the purse.
97 : */
98 : struct TALER_PrivateContractHashP h_contract_terms;
99 :
100 : /**
101 : * Fees that apply to this operation.
102 : */
103 : const struct TALER_WireFeeSet *wf;
104 :
105 : /**
106 : * URI of the account the purse is to be merged into.
107 : * Must be of the form 'payto://taler-reserve/$EXCHANGE_URL/RESERVE_PUB'.
108 : */
109 : const char *payto_uri;
110 :
111 : /**
112 : * Hash of the @e payto_uri.
113 : */
114 : struct TALER_PaytoHashP h_payto;
115 :
116 : /**
117 : * KYC status of the operation.
118 : */
119 : struct TALER_EXCHANGEDB_KycStatus kyc;
120 :
121 : /**
122 : * Base URL of the exchange provider hosting the reserve.
123 : */
124 : char *provider_url;
125 :
126 : /**
127 : * Minimum age for deposits into this purse.
128 : */
129 : uint32_t min_age;
130 : };
131 :
132 :
133 : /**
134 : * Send confirmation of purse creation success to client.
135 : *
136 : * @param connection connection to the client
137 : * @param pcc details about the request that succeeded
138 : * @return MHD result code
139 : */
140 : static MHD_RESULT
141 0 : reply_merge_success (struct MHD_Connection *connection,
142 : const struct PurseMergeContext *pcc)
143 : {
144 : struct TALER_ExchangePublicKeyP pub;
145 : struct TALER_ExchangeSignatureP sig;
146 : enum TALER_ErrorCode ec;
147 : struct TALER_Amount merge_amount;
148 :
149 0 : if (0 <
150 0 : TALER_amount_cmp (&pcc->balance,
151 : &pcc->target_amount))
152 : {
153 0 : GNUNET_break (0);
154 0 : return TALER_MHD_REPLY_JSON_PACK (
155 : connection,
156 : MHD_HTTP_INTERNAL_SERVER_ERROR,
157 : TALER_JSON_pack_amount ("balance",
158 : &pcc->balance),
159 : TALER_JSON_pack_amount ("target_amount",
160 : &pcc->target_amount));
161 : }
162 0 : if ( (NULL == pcc->provider_url) ||
163 0 : (0 == strcmp (pcc->provider_url,
164 : TEH_base_url)) )
165 : {
166 : /* wad fee is always zero if we stay at our own exchange */
167 0 : merge_amount = pcc->target_amount;
168 : }
169 : else
170 : {
171 0 : if (0 >
172 0 : TALER_amount_subtract (&merge_amount,
173 : &pcc->target_amount,
174 0 : &pcc->wf->wad))
175 : {
176 0 : GNUNET_assert (GNUNET_OK ==
177 : TALER_amount_set_zero (TEH_currency,
178 : &merge_amount));
179 : }
180 : }
181 0 : if (TALER_EC_NONE !=
182 0 : (ec = TALER_exchange_online_purse_merged_sign (
183 : &TEH_keys_exchange_sign_,
184 : pcc->exchange_timestamp,
185 : pcc->purse_expiration,
186 : &merge_amount,
187 : pcc->purse_pub,
188 : &pcc->h_contract_terms,
189 : &pcc->reserve_pub,
190 0 : (NULL != pcc->provider_url)
191 : ? pcc->provider_url
192 : : TEH_base_url,
193 : &pub,
194 : &sig)))
195 : {
196 0 : GNUNET_break (0);
197 0 : return TALER_MHD_reply_with_ec (connection,
198 : ec,
199 : NULL);
200 : }
201 0 : return TALER_MHD_REPLY_JSON_PACK (
202 : connection,
203 : MHD_HTTP_OK,
204 : TALER_JSON_pack_amount ("merge_amount",
205 : &merge_amount),
206 : GNUNET_JSON_pack_timestamp ("exchange_timestamp",
207 : pcc->exchange_timestamp),
208 : GNUNET_JSON_pack_data_auto ("exchange_sig",
209 : &sig),
210 : GNUNET_JSON_pack_data_auto ("exchange_pub",
211 : &pub));
212 : }
213 :
214 :
215 : /**
216 : * Function called to iterate over KYC-relevant
217 : * transaction amounts for a particular time range.
218 : * Called within a database transaction, so must
219 : * not start a new one.
220 : *
221 : * @param cls a `struct PurseMergeContext`
222 : * @param limit maximum time-range for which events
223 : * should be fetched (timestamp in the past)
224 : * @param cb function to call on each event found,
225 : * events must be returned in reverse chronological
226 : * order
227 : * @param cb_cls closure for @a cb
228 : */
229 : static void
230 0 : amount_iterator (void *cls,
231 : struct GNUNET_TIME_Absolute limit,
232 : TALER_EXCHANGEDB_KycAmountCallback cb,
233 : void *cb_cls)
234 : {
235 0 : struct PurseMergeContext *pcc = cls;
236 : enum GNUNET_DB_QueryStatus qs;
237 :
238 0 : cb (cb_cls,
239 0 : &pcc->target_amount,
240 : GNUNET_TIME_absolute_get ());
241 0 : qs = TEH_plugin->select_merge_amounts_for_kyc_check (
242 0 : TEH_plugin->cls,
243 0 : &pcc->h_payto,
244 : limit,
245 : cb,
246 : cb_cls);
247 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
248 : "Got %d additional transactions for this merge and limit %llu\n",
249 : qs,
250 : (unsigned long long) limit.abs_value_us);
251 0 : GNUNET_break (qs >= 0);
252 0 : }
253 :
254 :
255 : /**
256 : * Execute database transaction for /purses/$PID/merge. Runs the transaction
257 : * logic; IF it returns a non-error code, the transaction logic MUST NOT queue
258 : * a MHD response. IF it returns an hard error, the transaction logic MUST
259 : * queue a MHD response and set @a mhd_ret. IF it returns the soft error
260 : * code, the function MAY be called again to retry and MUST not queue a MHD
261 : * response.
262 : *
263 : * @param cls a `struct PurseMergeContext`
264 : * @param connection MHD request context
265 : * @param[out] mhd_ret set to MHD status on error
266 : * @return transaction status
267 : */
268 : static enum GNUNET_DB_QueryStatus
269 0 : merge_transaction (void *cls,
270 : struct MHD_Connection *connection,
271 : MHD_RESULT *mhd_ret)
272 : {
273 0 : struct PurseMergeContext *pcc = cls;
274 : enum GNUNET_DB_QueryStatus qs;
275 0 : bool in_conflict = true;
276 0 : bool no_balance = true;
277 0 : bool no_partner = true;
278 : const char *required;
279 :
280 0 : required = TALER_KYCLOGIC_kyc_test_required (
281 : TALER_KYCLOGIC_KYC_TRIGGER_P2P_RECEIVE,
282 0 : &pcc->h_payto,
283 0 : TEH_plugin->select_satisfied_kyc_processes,
284 0 : TEH_plugin->cls,
285 : &amount_iterator,
286 : pcc);
287 0 : if (NULL != required)
288 : {
289 0 : pcc->kyc.ok = false;
290 0 : return TEH_plugin->insert_kyc_requirement_for_account (
291 0 : TEH_plugin->cls,
292 : required,
293 0 : &pcc->h_payto,
294 : &pcc->kyc.requirement_row);
295 : }
296 0 : pcc->kyc.ok = true;
297 0 : qs = TEH_plugin->do_purse_merge (
298 0 : TEH_plugin->cls,
299 : pcc->purse_pub,
300 0 : &pcc->merge_sig,
301 : pcc->merge_timestamp,
302 0 : &pcc->reserve_sig,
303 0 : pcc->provider_url,
304 0 : &pcc->reserve_pub,
305 : &no_partner,
306 : &no_balance,
307 : &in_conflict);
308 0 : if (qs < 0)
309 : {
310 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
311 0 : return qs;
312 0 : TALER_LOG_WARNING (
313 : "Failed to store merge purse information in database\n");
314 0 : *mhd_ret =
315 0 : TALER_MHD_reply_with_error (connection,
316 : MHD_HTTP_INTERNAL_SERVER_ERROR,
317 : TALER_EC_GENERIC_DB_STORE_FAILED,
318 : "purse merge");
319 0 : return qs;
320 : }
321 0 : if (no_partner)
322 : {
323 0 : *mhd_ret =
324 0 : TALER_MHD_reply_with_error (connection,
325 : MHD_HTTP_NOT_FOUND,
326 : TALER_EC_EXCHANGE_MERGE_PURSE_PARTNER_UNKNOWN,
327 0 : pcc->provider_url);
328 0 : return GNUNET_DB_STATUS_HARD_ERROR;
329 : }
330 0 : if (no_balance)
331 : {
332 0 : *mhd_ret =
333 0 : TALER_MHD_reply_with_error (connection,
334 : MHD_HTTP_PAYMENT_REQUIRED,
335 : TALER_EC_EXCHANGE_PURSE_NOT_FULL,
336 : NULL);
337 0 : return GNUNET_DB_STATUS_HARD_ERROR;
338 : }
339 0 : if (in_conflict)
340 : {
341 : struct TALER_PurseMergeSignatureP merge_sig;
342 : struct GNUNET_TIME_Timestamp merge_timestamp;
343 0 : char *partner_url = NULL;
344 : struct TALER_ReservePublicKeyP reserve_pub;
345 :
346 0 : qs = TEH_plugin->select_purse_merge (TEH_plugin->cls,
347 : pcc->purse_pub,
348 : &merge_sig,
349 : &merge_timestamp,
350 : &partner_url,
351 : &reserve_pub);
352 0 : if (qs <= 0)
353 : {
354 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
355 0 : return qs;
356 0 : TALER_LOG_WARNING (
357 : "Failed to fetch merge purse information from database\n");
358 0 : *mhd_ret =
359 0 : TALER_MHD_reply_with_error (connection,
360 : MHD_HTTP_INTERNAL_SERVER_ERROR,
361 : TALER_EC_GENERIC_DB_FETCH_FAILED,
362 : "select purse merge");
363 0 : return qs;
364 : }
365 0 : *mhd_ret = TALER_MHD_REPLY_JSON_PACK (
366 : connection,
367 : MHD_HTTP_CONFLICT,
368 : GNUNET_JSON_pack_timestamp ("merge_timestamp",
369 : merge_timestamp),
370 : GNUNET_JSON_pack_data_auto ("merge_sig",
371 : &merge_sig),
372 : GNUNET_JSON_pack_allow_null (
373 : GNUNET_JSON_pack_string ("partner_url",
374 : partner_url)),
375 : GNUNET_JSON_pack_data_auto ("reserve_pub",
376 : &reserve_pub));
377 0 : GNUNET_free (partner_url);
378 0 : return GNUNET_DB_STATUS_HARD_ERROR;
379 : }
380 :
381 0 : return qs;
382 : }
383 :
384 :
385 : MHD_RESULT
386 0 : TEH_handler_purses_merge (
387 : struct MHD_Connection *connection,
388 : const struct TALER_PurseContractPublicKeyP *purse_pub,
389 : const json_t *root)
390 : {
391 0 : struct PurseMergeContext pcc = {
392 : .purse_pub = purse_pub,
393 0 : .exchange_timestamp = GNUNET_TIME_timestamp_get ()
394 : };
395 : struct GNUNET_JSON_Specification spec[] = {
396 0 : GNUNET_JSON_spec_string ("payto_uri",
397 : &pcc.payto_uri),
398 0 : GNUNET_JSON_spec_fixed_auto ("reserve_sig",
399 : &pcc.reserve_sig),
400 0 : GNUNET_JSON_spec_fixed_auto ("merge_sig",
401 : &pcc.merge_sig),
402 0 : GNUNET_JSON_spec_timestamp ("merge_timestamp",
403 : &pcc.merge_timestamp),
404 0 : GNUNET_JSON_spec_end ()
405 : };
406 : struct TALER_PurseContractSignatureP purse_sig;
407 : enum GNUNET_DB_QueryStatus qs;
408 : bool http;
409 :
410 : {
411 : enum GNUNET_GenericReturnValue res;
412 :
413 0 : res = TALER_MHD_parse_json_data (connection,
414 : root,
415 : spec);
416 0 : if (GNUNET_SYSERR == res)
417 : {
418 0 : GNUNET_break (0);
419 0 : return MHD_NO; /* hard failure */
420 : }
421 0 : if (GNUNET_NO == res)
422 : {
423 0 : GNUNET_break_op (0);
424 0 : return MHD_YES; /* failure */
425 : }
426 : }
427 :
428 : /* Fetch purse details */
429 0 : qs = TEH_plugin->select_purse_request (TEH_plugin->cls,
430 : pcc.purse_pub,
431 : &pcc.merge_pub,
432 : &pcc.purse_expiration,
433 : &pcc.h_contract_terms,
434 : &pcc.min_age,
435 : &pcc.target_amount,
436 : &pcc.balance,
437 : &purse_sig);
438 0 : switch (qs)
439 : {
440 0 : case GNUNET_DB_STATUS_HARD_ERROR:
441 0 : GNUNET_break (0);
442 0 : return TALER_MHD_reply_with_error (
443 : connection,
444 : MHD_HTTP_INTERNAL_SERVER_ERROR,
445 : TALER_EC_GENERIC_DB_FETCH_FAILED,
446 : "select purse request");
447 0 : case GNUNET_DB_STATUS_SOFT_ERROR:
448 0 : GNUNET_break (0);
449 0 : return TALER_MHD_reply_with_error (
450 : connection,
451 : MHD_HTTP_INTERNAL_SERVER_ERROR,
452 : TALER_EC_GENERIC_DB_FETCH_FAILED,
453 : "select purse request");
454 0 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
455 0 : return TALER_MHD_reply_with_error (
456 : connection,
457 : MHD_HTTP_NOT_FOUND,
458 : TALER_EC_EXCHANGE_GENERIC_PURSE_UNKNOWN,
459 : NULL);
460 0 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
461 : /* continued below */
462 0 : break;
463 : }
464 : /* parse 'payto_uri' into pcc.reserve_pub and provider_url */
465 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
466 : "Received payto: `%s'\n",
467 : pcc.payto_uri);
468 0 : if ( (0 != strncmp (pcc.payto_uri,
469 : "payto://taler-reserve/",
470 0 : strlen ("payto://taler-reserve/"))) &&
471 0 : (0 != strncmp (pcc.payto_uri,
472 : "payto://taler-reserve-http/",
473 : strlen ("payto://taler-reserve+http/"))) )
474 : {
475 0 : GNUNET_break_op (0);
476 0 : return TALER_MHD_reply_with_error (
477 : connection,
478 : MHD_HTTP_BAD_REQUEST,
479 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
480 : "payto_uri");
481 : }
482 0 : http = (0 == strncmp (pcc.payto_uri,
483 : "payto://taler-reserve-http/",
484 : strlen ("payto://taler-reserve-http/")));
485 :
486 : {
487 0 : const char *host = &pcc.payto_uri[http
488 : ? strlen ("payto://taler-reserve-http/")
489 0 : : strlen ("payto://taler-reserve/")];
490 0 : const char *slash = strchr (host,
491 : '/');
492 :
493 0 : if (NULL == slash)
494 : {
495 0 : GNUNET_break_op (0);
496 0 : return TALER_MHD_reply_with_error (
497 : connection,
498 : MHD_HTTP_BAD_REQUEST,
499 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
500 : "payto_uri");
501 : }
502 0 : GNUNET_asprintf (&pcc.provider_url,
503 : "%s://%.*s/",
504 : http ? "http" : "https",
505 0 : (int) (slash - host),
506 : host);
507 0 : slash++;
508 0 : if (GNUNET_OK !=
509 0 : GNUNET_STRINGS_string_to_data (slash,
510 : strlen (slash),
511 : &pcc.reserve_pub,
512 : sizeof (pcc.reserve_pub)))
513 : {
514 0 : GNUNET_break_op (0);
515 0 : GNUNET_free (pcc.provider_url);
516 0 : return TALER_MHD_reply_with_error (
517 : connection,
518 : MHD_HTTP_BAD_REQUEST,
519 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
520 : "payto_uri");
521 : }
522 0 : slash++;
523 : }
524 0 : TALER_payto_hash (pcc.payto_uri,
525 : &pcc.h_payto);
526 0 : if (0 == strcmp (pcc.provider_url,
527 : TEH_base_url))
528 : {
529 : /* we use NULL to represent 'self' as the provider */
530 0 : GNUNET_free (pcc.provider_url);
531 : }
532 : else
533 : {
534 0 : char *method = GNUNET_strdup ("FIXME-WAD #7271");
535 :
536 : /* FIXME-#7271: lookup wire method by pcc.provider_url! */
537 0 : pcc.wf = TEH_wire_fees_by_time (pcc.exchange_timestamp,
538 : method);
539 0 : if (NULL == pcc.wf)
540 : {
541 : MHD_RESULT res;
542 :
543 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
544 : "Cannot merge purse: wire fees not configured!\n");
545 0 : res = TALER_MHD_reply_with_error (connection,
546 : MHD_HTTP_INTERNAL_SERVER_ERROR,
547 : TALER_EC_EXCHANGE_GENERIC_WIRE_FEES_MISSING,
548 : method);
549 0 : GNUNET_free (method);
550 0 : return res;
551 : }
552 0 : GNUNET_free (method);
553 : }
554 : /* check signatures */
555 0 : if (GNUNET_OK !=
556 0 : TALER_wallet_purse_merge_verify (
557 : pcc.payto_uri,
558 : pcc.merge_timestamp,
559 : pcc.purse_pub,
560 : &pcc.merge_pub,
561 : &pcc.merge_sig))
562 : {
563 0 : GNUNET_break_op (0);
564 0 : GNUNET_free (pcc.provider_url);
565 0 : return TALER_MHD_reply_with_error (
566 : connection,
567 : MHD_HTTP_FORBIDDEN,
568 : TALER_EC_EXCHANGE_PURSE_MERGE_INVALID_MERGE_SIGNATURE,
569 : NULL);
570 : }
571 : {
572 : struct TALER_Amount zero_purse_fee;
573 :
574 0 : GNUNET_assert (GNUNET_OK ==
575 : TALER_amount_set_zero (pcc.target_amount.currency,
576 : &zero_purse_fee));
577 0 : if (GNUNET_OK !=
578 0 : TALER_wallet_account_merge_verify (
579 : pcc.merge_timestamp,
580 : pcc.purse_pub,
581 : pcc.purse_expiration,
582 : &pcc.h_contract_terms,
583 : &pcc.target_amount,
584 : &zero_purse_fee,
585 : pcc.min_age,
586 : TALER_WAMF_MODE_MERGE_FULLY_PAID_PURSE,
587 : &pcc.reserve_pub,
588 : &pcc.reserve_sig))
589 : {
590 0 : GNUNET_break_op (0);
591 0 : GNUNET_free (pcc.provider_url);
592 0 : return TALER_MHD_reply_with_error (
593 : connection,
594 : MHD_HTTP_FORBIDDEN,
595 : TALER_EC_EXCHANGE_PURSE_MERGE_INVALID_RESERVE_SIGNATURE,
596 : NULL);
597 : }
598 : }
599 :
600 0 : if (GNUNET_TIME_absolute_is_past (pcc.purse_expiration.abs_time))
601 : {
602 : struct TALER_PurseMergeSignatureP merge_sig;
603 : struct GNUNET_TIME_Timestamp merge_timestamp;
604 0 : char *partner_url = NULL;
605 : struct TALER_ReservePublicKeyP reserve_pub;
606 :
607 0 : qs = TEH_plugin->select_purse_merge (TEH_plugin->cls,
608 : pcc.purse_pub,
609 : &merge_sig,
610 : &merge_timestamp,
611 : &partner_url,
612 : &reserve_pub);
613 0 : if (qs <= 0)
614 : {
615 0 : return TALER_MHD_reply_with_error (connection,
616 : MHD_HTTP_GONE,
617 : TALER_EC_EXCHANGE_GENERIC_PURSE_EXPIRED,
618 : NULL);
619 : }
620 0 : if (0 !=
621 0 : GNUNET_memcmp (&merge_sig,
622 : &pcc.merge_sig))
623 : {
624 : MHD_RESULT mhd_res;
625 :
626 0 : mhd_res = TALER_MHD_REPLY_JSON_PACK (
627 : connection,
628 : MHD_HTTP_CONFLICT,
629 : GNUNET_JSON_pack_timestamp ("merge_timestamp",
630 : merge_timestamp),
631 : GNUNET_JSON_pack_data_auto ("merge_sig",
632 : &merge_sig),
633 : GNUNET_JSON_pack_allow_null (
634 : GNUNET_JSON_pack_string ("partner_url",
635 : partner_url)),
636 : GNUNET_JSON_pack_data_auto ("reserve_pub",
637 : &reserve_pub));
638 0 : GNUNET_free (partner_url);
639 0 : return mhd_res;
640 : }
641 0 : GNUNET_free (partner_url);
642 : /* request was idempotent, return success! */
643 0 : return reply_merge_success (connection,
644 : &pcc);
645 : }
646 :
647 : /* execute transaction */
648 : {
649 : MHD_RESULT mhd_ret;
650 :
651 0 : if (GNUNET_OK !=
652 0 : TEH_DB_run_transaction (connection,
653 : "execute purse merge",
654 : TEH_MT_REQUEST_PURSE_MERGE,
655 : &mhd_ret,
656 : &merge_transaction,
657 : &pcc))
658 : {
659 0 : GNUNET_free (pcc.provider_url);
660 0 : return mhd_ret;
661 : }
662 : }
663 :
664 :
665 0 : GNUNET_free (pcc.provider_url);
666 0 : if (! pcc.kyc.ok)
667 0 : return TEH_RESPONSE_reply_kyc_required (connection,
668 : &pcc.h_payto,
669 : &pcc.kyc);
670 :
671 : {
672 0 : struct TALER_PurseEventP rep = {
673 0 : .header.size = htons (sizeof (rep)),
674 0 : .header.type = htons (TALER_DBEVENT_EXCHANGE_PURSE_MERGED),
675 0 : .purse_pub = *pcc.purse_pub
676 : };
677 :
678 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
679 : "Notifying about purse merge\n");
680 0 : TEH_plugin->event_notify (TEH_plugin->cls,
681 : &rep.header,
682 : NULL,
683 : 0);
684 : }
685 :
686 : /* generate regular response */
687 0 : return reply_merge_success (connection,
688 : &pcc);
689 : }
690 :
691 :
692 : /* end of taler-exchange-httpd_purses_merge.c */
|