Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2014-2023 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_coins_get.c
18 : * @brief Handle GET /coins/$COIN_PUB/history requests
19 : * @author Christian Grothoff
20 : */
21 : #include "taler/platform.h"
22 : #include <gnunet/gnunet_util_lib.h>
23 : #include <jansson.h>
24 : #include "taler/taler_mhd_lib.h"
25 : #include "taler/taler_json_lib.h"
26 : #include "taler/taler_dbevents.h"
27 : #include "taler-exchange-httpd_keys.h"
28 : #include "taler-exchange-httpd_coins_get.h"
29 : #include "taler-exchange-httpd_responses.h"
30 :
31 :
32 : /**
33 : * Add the headers we want to set for every response.
34 : *
35 : * @param cls the key state to use
36 : * @param[in,out] response the response to modify
37 : */
38 : static void
39 4 : add_response_headers (void *cls,
40 : struct MHD_Response *response)
41 : {
42 : (void) cls;
43 4 : GNUNET_break (MHD_YES ==
44 : MHD_add_response_header (response,
45 : MHD_HTTP_HEADER_CACHE_CONTROL,
46 : "no-cache"));
47 4 : }
48 :
49 :
50 : /**
51 : * Compile the transaction history of a coin into a JSON object.
52 : *
53 : * @param coin_pub public key of the coin
54 : * @param tl transaction history to JSON-ify
55 : * @return json representation of the @a rh, NULL on error
56 : */
57 : static json_t *
58 4 : compile_transaction_history (
59 : const struct TALER_CoinSpendPublicKeyP *coin_pub,
60 : const struct TALER_EXCHANGEDB_TransactionList *tl)
61 : {
62 : json_t *history;
63 :
64 4 : history = json_array ();
65 4 : if (NULL == history)
66 : {
67 0 : GNUNET_break (0); /* out of memory!? */
68 0 : return NULL;
69 : }
70 4 : for (const struct TALER_EXCHANGEDB_TransactionList *pos = tl;
71 9 : NULL != pos;
72 5 : pos = pos->next)
73 : {
74 5 : switch (pos->type)
75 : {
76 2 : case TALER_EXCHANGEDB_TT_DEPOSIT:
77 : {
78 2 : const struct TALER_EXCHANGEDB_DepositListEntry *deposit =
79 : pos->details.deposit;
80 : struct TALER_MerchantWireHashP h_wire;
81 :
82 2 : TALER_merchant_wire_signature_hash (deposit->receiver_wire_account,
83 : &deposit->wire_salt,
84 : &h_wire);
85 : #if ENABLE_SANITY_CHECKS
86 : /* internal sanity check before we hand out a bogus sig... */
87 2 : TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
88 2 : if (GNUNET_OK !=
89 4 : TALER_wallet_deposit_verify (
90 : &deposit->amount_with_fee,
91 : &deposit->deposit_fee,
92 : &h_wire,
93 : &deposit->h_contract_terms,
94 2 : deposit->no_wallet_data_hash
95 : ? NULL
96 : : &deposit->wallet_data_hash,
97 2 : deposit->no_age_commitment
98 : ? NULL
99 : : &deposit->h_age_commitment,
100 : &deposit->h_policy,
101 : &deposit->h_denom_pub,
102 : deposit->timestamp,
103 : &deposit->merchant_pub,
104 : deposit->refund_deadline,
105 : coin_pub,
106 : &deposit->csig))
107 : {
108 0 : GNUNET_break (0);
109 0 : json_decref (history);
110 0 : return NULL;
111 : }
112 : #endif
113 2 : if (0 !=
114 2 : json_array_append_new (
115 : history,
116 2 : GNUNET_JSON_PACK (
117 : GNUNET_JSON_pack_string ("type",
118 : "DEPOSIT"),
119 : GNUNET_JSON_pack_uint64 ("history_offset",
120 : pos->coin_history_id),
121 : TALER_JSON_pack_amount ("amount",
122 : &deposit->amount_with_fee),
123 : TALER_JSON_pack_amount ("deposit_fee",
124 : &deposit->deposit_fee),
125 : GNUNET_JSON_pack_data_auto ("merchant_pub",
126 : &deposit->merchant_pub),
127 : GNUNET_JSON_pack_timestamp ("timestamp",
128 : deposit->timestamp),
129 : GNUNET_JSON_pack_allow_null (
130 : GNUNET_JSON_pack_timestamp ("refund_deadline",
131 : deposit->refund_deadline)),
132 : GNUNET_JSON_pack_data_auto ("h_contract_terms",
133 : &deposit->h_contract_terms),
134 : GNUNET_JSON_pack_data_auto ("h_wire",
135 : &h_wire),
136 : GNUNET_JSON_pack_data_auto ("h_denom_pub",
137 : &deposit->h_denom_pub),
138 : GNUNET_JSON_pack_allow_null (
139 : deposit->has_policy
140 : ? GNUNET_JSON_pack_data_auto ("h_policy",
141 : &deposit->h_policy)
142 : : GNUNET_JSON_pack_string (
143 : "h_policy",
144 : NULL)),
145 : GNUNET_JSON_pack_allow_null (
146 : deposit->no_wallet_data_hash
147 : ? GNUNET_JSON_pack_string (
148 : "wallet_data_hash",
149 : NULL)
150 : : GNUNET_JSON_pack_data_auto ("wallet_data_hash",
151 : &deposit->wallet_data_hash)),
152 : GNUNET_JSON_pack_allow_null (
153 : deposit->no_age_commitment
154 : ? GNUNET_JSON_pack_string (
155 : "h_age_commitment",
156 : NULL)
157 : : GNUNET_JSON_pack_data_auto ("h_age_commitment",
158 : &deposit->h_age_commitment)),
159 : GNUNET_JSON_pack_data_auto ("coin_sig",
160 : &deposit->csig))))
161 : {
162 0 : GNUNET_break (0);
163 0 : json_decref (history);
164 0 : return NULL;
165 : }
166 2 : break;
167 : }
168 0 : case TALER_EXCHANGEDB_TT_MELT:
169 : {
170 0 : const struct TALER_EXCHANGEDB_MeltListEntry *melt =
171 : pos->details.melt;
172 : const struct TALER_AgeCommitmentHashP *phac;
173 : const struct TALER_BlindingMasterSeedP *pbs;
174 :
175 : #if ENABLE_SANITY_CHECKS
176 0 : TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
177 0 : if (GNUNET_OK !=
178 0 : TALER_wallet_melt_verify (
179 : &melt->amount_with_fee,
180 : &melt->melt_fee,
181 : &melt->rc,
182 : &melt->h_denom_pub,
183 : &melt->h_age_commitment,
184 : coin_pub,
185 : &melt->coin_sig))
186 : {
187 0 : GNUNET_break (0);
188 0 : json_decref (history);
189 0 : return NULL;
190 : }
191 : #endif
192 0 : phac = (melt->no_age_commitment)
193 : ? NULL
194 0 : : &melt->h_age_commitment;
195 0 : pbs = melt->no_blinding_seed
196 : ? NULL
197 0 : : &melt->blinding_seed;
198 0 : if (0 !=
199 0 : json_array_append_new (
200 : history,
201 0 : GNUNET_JSON_PACK (
202 : GNUNET_JSON_pack_string ("type",
203 : "MELT"),
204 : GNUNET_JSON_pack_uint64 ("history_offset",
205 : pos->coin_history_id),
206 : TALER_JSON_pack_amount ("amount",
207 : &melt->amount_with_fee),
208 : TALER_JSON_pack_amount ("melt_fee",
209 : &melt->melt_fee),
210 : GNUNET_JSON_pack_data_auto ("rc",
211 : &melt->rc),
212 : GNUNET_JSON_pack_data_auto ("h_denom_pub",
213 : &melt->h_denom_pub),
214 : GNUNET_JSON_pack_data_auto ("refresh_seed",
215 : &melt->refresh_seed),
216 : GNUNET_JSON_pack_allow_null (
217 : GNUNET_JSON_pack_data_auto ("blinding_seed",
218 : pbs)),
219 : GNUNET_JSON_pack_allow_null (
220 : GNUNET_JSON_pack_data_auto ("h_age_commitment",
221 : phac)),
222 : GNUNET_JSON_pack_data_auto ("coin_sig",
223 : &melt->coin_sig))))
224 : {
225 0 : GNUNET_break (0);
226 0 : json_decref (history);
227 0 : return NULL;
228 : }
229 : }
230 0 : break;
231 0 : case TALER_EXCHANGEDB_TT_REFUND:
232 : {
233 0 : const struct TALER_EXCHANGEDB_RefundListEntry *refund =
234 : pos->details.refund;
235 : struct TALER_Amount value;
236 :
237 : #if ENABLE_SANITY_CHECKS
238 0 : TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
239 0 : if (GNUNET_OK !=
240 0 : TALER_merchant_refund_verify (
241 : coin_pub,
242 : &refund->h_contract_terms,
243 0 : refund->rtransaction_id,
244 : &refund->refund_amount,
245 : &refund->merchant_pub,
246 : &refund->merchant_sig))
247 : {
248 0 : GNUNET_break (0);
249 0 : json_decref (history);
250 0 : return NULL;
251 : }
252 : #endif
253 0 : if (0 >
254 0 : TALER_amount_subtract (&value,
255 : &refund->refund_amount,
256 : &refund->refund_fee))
257 : {
258 0 : GNUNET_break (0);
259 0 : json_decref (history);
260 0 : return NULL;
261 : }
262 0 : if (0 !=
263 0 : json_array_append_new (
264 : history,
265 0 : GNUNET_JSON_PACK (
266 : GNUNET_JSON_pack_string ("type",
267 : "REFUND"),
268 : GNUNET_JSON_pack_uint64 ("history_offset",
269 : pos->coin_history_id),
270 : TALER_JSON_pack_amount ("amount",
271 : &value),
272 : TALER_JSON_pack_amount ("refund_fee",
273 : &refund->refund_fee),
274 : GNUNET_JSON_pack_data_auto ("h_contract_terms",
275 : &refund->h_contract_terms),
276 : GNUNET_JSON_pack_data_auto ("merchant_pub",
277 : &refund->merchant_pub),
278 : GNUNET_JSON_pack_uint64 ("rtransaction_id",
279 : refund->rtransaction_id),
280 : GNUNET_JSON_pack_data_auto ("merchant_sig",
281 : &refund->merchant_sig))))
282 : {
283 0 : GNUNET_break (0);
284 0 : json_decref (history);
285 0 : return NULL;
286 : }
287 : }
288 0 : break;
289 0 : case TALER_EXCHANGEDB_TT_OLD_COIN_RECOUP:
290 : {
291 0 : struct TALER_EXCHANGEDB_RecoupRefreshListEntry *pr =
292 : pos->details.old_coin_recoup;
293 : struct TALER_ExchangePublicKeyP epub;
294 : struct TALER_ExchangeSignatureP esig;
295 :
296 0 : if (TALER_EC_NONE !=
297 0 : TALER_exchange_online_confirm_recoup_refresh_sign (
298 : &TEH_keys_exchange_sign_,
299 : pr->timestamp,
300 0 : &pr->value,
301 0 : &pr->coin.coin_pub,
302 0 : &pr->old_coin_pub,
303 : &epub,
304 : &esig))
305 : {
306 0 : GNUNET_break (0);
307 0 : json_decref (history);
308 0 : return NULL;
309 : }
310 : /* NOTE: we could also provide coin_pub's coin_sig, denomination key hash and
311 : the denomination key's RSA signature over coin_pub, but as the
312 : wallet should really already have this information (and cannot
313 : check or do anything with it anyway if it doesn't), it seems
314 : strictly unnecessary. */
315 0 : if (0 !=
316 0 : json_array_append_new (
317 : history,
318 0 : GNUNET_JSON_PACK (
319 : GNUNET_JSON_pack_string ("type",
320 : "OLD-COIN-RECOUP"),
321 : GNUNET_JSON_pack_uint64 ("history_offset",
322 : pos->coin_history_id),
323 : TALER_JSON_pack_amount ("amount",
324 : &pr->value),
325 : GNUNET_JSON_pack_data_auto ("exchange_sig",
326 : &esig),
327 : GNUNET_JSON_pack_data_auto ("exchange_pub",
328 : &epub),
329 : GNUNET_JSON_pack_data_auto ("coin_pub",
330 : &pr->coin.coin_pub),
331 : GNUNET_JSON_pack_timestamp ("timestamp",
332 : pr->timestamp))))
333 : {
334 0 : GNUNET_break (0);
335 0 : json_decref (history);
336 0 : return NULL;
337 : }
338 0 : break;
339 : }
340 0 : case TALER_EXCHANGEDB_TT_RECOUP:
341 : {
342 0 : const struct TALER_EXCHANGEDB_RecoupListEntry *recoup =
343 : pos->details.recoup;
344 : struct TALER_ExchangePublicKeyP epub;
345 : struct TALER_ExchangeSignatureP esig;
346 :
347 0 : if (TALER_EC_NONE !=
348 0 : TALER_exchange_online_confirm_recoup_sign (
349 : &TEH_keys_exchange_sign_,
350 : recoup->timestamp,
351 : &recoup->value,
352 : coin_pub,
353 : &recoup->reserve_pub,
354 : &epub,
355 : &esig))
356 : {
357 0 : GNUNET_break (0);
358 0 : json_decref (history);
359 0 : return NULL;
360 : }
361 0 : if (0 !=
362 0 : json_array_append_new (
363 : history,
364 0 : GNUNET_JSON_PACK (
365 : GNUNET_JSON_pack_string ("type",
366 : "RECOUP"),
367 : GNUNET_JSON_pack_uint64 ("history_offset",
368 : pos->coin_history_id),
369 : TALER_JSON_pack_amount ("amount",
370 : &recoup->value),
371 : GNUNET_JSON_pack_data_auto ("exchange_sig",
372 : &esig),
373 : GNUNET_JSON_pack_data_auto ("exchange_pub",
374 : &epub),
375 : GNUNET_JSON_pack_data_auto ("reserve_pub",
376 : &recoup->reserve_pub),
377 : GNUNET_JSON_pack_data_auto ("coin_sig",
378 : &recoup->coin_sig),
379 : GNUNET_JSON_pack_data_auto ("h_denom_pub",
380 : &recoup->h_denom_pub),
381 : GNUNET_JSON_pack_data_auto ("coin_blind",
382 : &recoup->coin_blind),
383 : // FIXME-9828: spec says we should have h_commitment?
384 : // FIXME-9828: spec says we should have coin_index?
385 : GNUNET_JSON_pack_data_auto ("reserve_pub",
386 : &recoup->reserve_pub),
387 : GNUNET_JSON_pack_timestamp ("timestamp",
388 : recoup->timestamp))))
389 : {
390 0 : GNUNET_break (0);
391 0 : json_decref (history);
392 0 : return NULL;
393 : }
394 : }
395 0 : break;
396 0 : case TALER_EXCHANGEDB_TT_RECOUP_REFRESH:
397 : {
398 0 : struct TALER_EXCHANGEDB_RecoupRefreshListEntry *pr =
399 : pos->details.recoup_refresh;
400 : struct TALER_ExchangePublicKeyP epub;
401 : struct TALER_ExchangeSignatureP esig;
402 :
403 0 : if (TALER_EC_NONE !=
404 0 : TALER_exchange_online_confirm_recoup_refresh_sign (
405 : &TEH_keys_exchange_sign_,
406 : pr->timestamp,
407 0 : &pr->value,
408 : coin_pub,
409 0 : &pr->old_coin_pub,
410 : &epub,
411 : &esig))
412 : {
413 0 : GNUNET_break (0);
414 0 : json_decref (history);
415 0 : return NULL;
416 : }
417 : /* NOTE: we could also provide coin_pub's coin_sig, denomination key
418 : hash and the denomination key's RSA signature over coin_pub, but as
419 : the wallet should really already have this information (and cannot
420 : check or do anything with it anyway if it doesn't), it seems
421 : strictly unnecessary. */
422 0 : if (0 !=
423 0 : json_array_append_new (
424 : history,
425 0 : GNUNET_JSON_PACK (
426 : GNUNET_JSON_pack_string ("type",
427 : "RECOUP-REFRESH"),
428 : GNUNET_JSON_pack_uint64 ("history_offset",
429 : pos->coin_history_id),
430 : TALER_JSON_pack_amount ("amount",
431 : &pr->value),
432 : GNUNET_JSON_pack_data_auto ("exchange_sig",
433 : &esig),
434 : GNUNET_JSON_pack_data_auto ("exchange_pub",
435 : &epub),
436 : GNUNET_JSON_pack_data_auto ("old_coin_pub",
437 : &pr->old_coin_pub),
438 : GNUNET_JSON_pack_data_auto ("coin_sig",
439 : &pr->coin_sig),
440 : // FIXME-#9828: spec says to return h_denom_pub
441 : GNUNET_JSON_pack_data_auto ("coin_blind",
442 : &pr->coin_blind),
443 : // FIXME-#9828: spec says to return h_commitment
444 : // FIXME-#9828: spec says to return coin_index
445 : // FIXME-#9828: spec says to return new_coin_blinding_secret
446 : // FIXME-#9828: spec says to return new_coin_ev
447 : GNUNET_JSON_pack_timestamp ("timestamp",
448 : pr->timestamp))))
449 : {
450 0 : GNUNET_break (0);
451 0 : json_decref (history);
452 0 : return NULL;
453 : }
454 0 : break;
455 : }
456 :
457 3 : case TALER_EXCHANGEDB_TT_PURSE_DEPOSIT:
458 : {
459 3 : struct TALER_EXCHANGEDB_PurseDepositListEntry *pd
460 : = pos->details.purse_deposit;
461 3 : const struct TALER_AgeCommitmentHashP *phac = NULL;
462 :
463 3 : if (! pd->no_age_commitment)
464 0 : phac = &pd->h_age_commitment;
465 3 : if (0 !=
466 3 : json_array_append_new (
467 : history,
468 3 : GNUNET_JSON_PACK (
469 : GNUNET_JSON_pack_string ("type",
470 : "PURSE-DEPOSIT"),
471 : GNUNET_JSON_pack_uint64 ("history_offset",
472 : pos->coin_history_id),
473 : TALER_JSON_pack_amount ("amount",
474 : &pd->amount),
475 : GNUNET_JSON_pack_string ("exchange_base_url",
476 : NULL == pd->exchange_base_url
477 : ? TEH_base_url
478 : : pd->exchange_base_url),
479 : GNUNET_JSON_pack_allow_null (
480 : GNUNET_JSON_pack_data_auto ("h_age_commitment",
481 : phac)),
482 : TALER_JSON_pack_amount ("deposit_fee",
483 : &pd->deposit_fee),
484 : GNUNET_JSON_pack_data_auto ("purse_pub",
485 : &pd->purse_pub),
486 : GNUNET_JSON_pack_bool ("refunded",
487 : pd->refunded),
488 : GNUNET_JSON_pack_data_auto ("coin_sig",
489 : &pd->coin_sig),
490 : GNUNET_JSON_pack_data_auto ("h_denom_pub",
491 : &pd->h_denom_pub))))
492 : {
493 0 : GNUNET_break (0);
494 0 : json_decref (history);
495 0 : return NULL;
496 : }
497 3 : break;
498 : }
499 :
500 0 : case TALER_EXCHANGEDB_TT_PURSE_REFUND:
501 : {
502 0 : const struct TALER_EXCHANGEDB_PurseRefundListEntry *prefund =
503 : pos->details.purse_refund;
504 : struct TALER_Amount value;
505 : enum TALER_ErrorCode ec;
506 : struct TALER_ExchangePublicKeyP epub;
507 : struct TALER_ExchangeSignatureP esig;
508 :
509 0 : if (0 >
510 0 : TALER_amount_subtract (&value,
511 : &prefund->refund_amount,
512 : &prefund->refund_fee))
513 : {
514 0 : GNUNET_break (0);
515 0 : json_decref (history);
516 0 : return NULL;
517 : }
518 0 : ec = TALER_exchange_online_purse_refund_sign (
519 : &TEH_keys_exchange_sign_,
520 : &value,
521 : &prefund->refund_fee,
522 : coin_pub,
523 : &prefund->purse_pub,
524 : &epub,
525 : &esig);
526 0 : if (TALER_EC_NONE != ec)
527 : {
528 0 : GNUNET_break (0);
529 0 : json_decref (history);
530 0 : return NULL;
531 : }
532 0 : if (0 !=
533 0 : json_array_append_new (
534 : history,
535 0 : GNUNET_JSON_PACK (
536 : GNUNET_JSON_pack_string ("type",
537 : "PURSE-REFUND"),
538 : GNUNET_JSON_pack_uint64 ("history_offset",
539 : pos->coin_history_id),
540 : TALER_JSON_pack_amount ("amount",
541 : &value),
542 : TALER_JSON_pack_amount ("refund_fee",
543 : &prefund->refund_fee),
544 : GNUNET_JSON_pack_data_auto ("exchange_sig",
545 : &esig),
546 : GNUNET_JSON_pack_data_auto ("exchange_pub",
547 : &epub),
548 : GNUNET_JSON_pack_data_auto ("purse_pub",
549 : &prefund->purse_pub))))
550 : {
551 0 : GNUNET_break (0);
552 0 : json_decref (history);
553 0 : return NULL;
554 : }
555 : }
556 0 : break;
557 :
558 0 : case TALER_EXCHANGEDB_TT_RESERVE_OPEN:
559 : {
560 0 : struct TALER_EXCHANGEDB_ReserveOpenListEntry *role
561 : = pos->details.reserve_open;
562 :
563 0 : if (0 !=
564 0 : json_array_append_new (
565 : history,
566 0 : GNUNET_JSON_PACK (
567 : GNUNET_JSON_pack_string ("type",
568 : "RESERVE-OPEN-DEPOSIT"),
569 : GNUNET_JSON_pack_uint64 ("history_offset",
570 : pos->coin_history_id),
571 : TALER_JSON_pack_amount ("coin_contribution",
572 : &role->coin_contribution),
573 : GNUNET_JSON_pack_data_auto ("reserve_sig",
574 : &role->reserve_sig),
575 : GNUNET_JSON_pack_data_auto ("coin_sig",
576 : &role->coin_sig))))
577 : {
578 0 : GNUNET_break (0);
579 0 : json_decref (history);
580 0 : return NULL;
581 : }
582 0 : break;
583 : }
584 : }
585 : }
586 4 : return history;
587 : }
588 :
589 :
590 : MHD_RESULT
591 4 : TEH_handler_coins_get (struct TEH_RequestContext *rc,
592 : const struct TALER_CoinSpendPublicKeyP *coin_pub)
593 : {
594 4 : struct TALER_EXCHANGEDB_TransactionList *tl = NULL;
595 4 : uint64_t start_off = 0;
596 : uint64_t etag_in;
597 : uint64_t etag_out;
598 : char etagp[24];
599 : struct MHD_Response *resp;
600 : unsigned int http_status;
601 : struct TALER_DenominationHashP h_denom_pub;
602 : struct TALER_Amount balance;
603 :
604 4 : TALER_MHD_parse_request_number (rc->connection,
605 : "start",
606 : &start_off);
607 : /* Check signature */
608 : {
609 : struct TALER_CoinSpendSignatureP coin_sig;
610 4 : bool required = true;
611 :
612 4 : TALER_MHD_parse_request_header_auto (rc->connection,
613 : TALER_COIN_HISTORY_SIGNATURE_HEADER,
614 : &coin_sig,
615 : required);
616 4 : if (GNUNET_OK !=
617 4 : TALER_wallet_coin_history_verify (start_off,
618 : coin_pub,
619 : &coin_sig))
620 : {
621 0 : GNUNET_break_op (0);
622 0 : return TALER_MHD_reply_with_error (rc->connection,
623 : MHD_HTTP_FORBIDDEN,
624 : TALER_EC_EXCHANGE_COIN_HISTORY_BAD_SIGNATURE,
625 : NULL);
626 : }
627 : }
628 :
629 : /* Get etag */
630 : {
631 : const char *etags;
632 :
633 4 : etags = MHD_lookup_connection_value (rc->connection,
634 : MHD_HEADER_KIND,
635 : MHD_HTTP_HEADER_IF_NONE_MATCH);
636 4 : if (NULL != etags)
637 : {
638 : char dummy;
639 : unsigned long long ev;
640 :
641 0 : if (1 != sscanf (etags,
642 : "\"%llu\"%c",
643 : &ev,
644 : &dummy))
645 : {
646 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
647 : "Client send malformed `If-None-Match' header `%s'\n",
648 : etags);
649 0 : etag_in = start_off;
650 : }
651 : else
652 : {
653 0 : etag_in = (uint64_t) ev;
654 : }
655 : }
656 : else
657 : {
658 4 : etag_in = start_off;
659 : }
660 : }
661 :
662 : /* Get history from DB between etag and now */
663 : {
664 : enum GNUNET_DB_QueryStatus qs;
665 :
666 4 : qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
667 : true,
668 : coin_pub,
669 : start_off,
670 : etag_in,
671 : &etag_out,
672 : &balance,
673 : &h_denom_pub,
674 : &tl);
675 4 : switch (qs)
676 : {
677 0 : case GNUNET_DB_STATUS_HARD_ERROR:
678 0 : GNUNET_break (0);
679 0 : return TALER_MHD_reply_with_error (rc->connection,
680 : MHD_HTTP_INTERNAL_SERVER_ERROR,
681 : TALER_EC_GENERIC_DB_FETCH_FAILED,
682 : "get_coin_history");
683 0 : case GNUNET_DB_STATUS_SOFT_ERROR:
684 0 : return TALER_MHD_reply_with_error (rc->connection,
685 : MHD_HTTP_INTERNAL_SERVER_ERROR,
686 : TALER_EC_GENERIC_DB_SOFT_FAILURE,
687 : "get_coin_history");
688 0 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
689 0 : return TALER_MHD_reply_with_error (rc->connection,
690 : MHD_HTTP_NOT_FOUND,
691 : TALER_EC_EXCHANGE_GENERIC_COIN_UNKNOWN,
692 : NULL);
693 4 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
694 : /* Handled below */
695 4 : break;
696 : }
697 : }
698 :
699 4 : GNUNET_snprintf (etagp,
700 : sizeof (etagp),
701 : "\"%llu\"",
702 : (unsigned long long) etag_out);
703 4 : if (etag_in == etag_out)
704 : {
705 0 : return TEH_RESPONSE_reply_not_modified (rc->connection,
706 : etagp,
707 : &add_response_headers,
708 : NULL);
709 : }
710 4 : if (NULL == tl)
711 : {
712 : /* 204: empty history */
713 0 : resp = MHD_create_response_from_buffer_static (0,
714 : "");
715 0 : http_status = MHD_HTTP_NO_CONTENT;
716 : }
717 : else
718 : {
719 : /* 200: regular history */
720 : json_t *history;
721 :
722 4 : history = compile_transaction_history (coin_pub,
723 : tl);
724 4 : TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
725 : tl);
726 4 : tl = NULL;
727 4 : if (NULL == history)
728 : {
729 0 : GNUNET_break (0);
730 0 : return TALER_MHD_reply_with_error (rc->connection,
731 : MHD_HTTP_INTERNAL_SERVER_ERROR,
732 : TALER_EC_GENERIC_JSON_ALLOCATION_FAILURE,
733 : "Failed to compile coin history");
734 : }
735 4 : resp = TALER_MHD_MAKE_JSON_PACK (
736 : GNUNET_JSON_pack_data_auto ("h_denom_pub",
737 : &h_denom_pub),
738 : TALER_JSON_pack_amount ("balance",
739 : &balance),
740 : GNUNET_JSON_pack_array_steal ("history",
741 : history));
742 4 : http_status = MHD_HTTP_OK;
743 : }
744 4 : add_response_headers (NULL,
745 : resp);
746 4 : GNUNET_break (MHD_YES ==
747 : MHD_add_response_header (resp,
748 : MHD_HTTP_HEADER_ETAG,
749 : etagp));
750 : {
751 : MHD_RESULT ret;
752 :
753 4 : ret = MHD_queue_response (rc->connection,
754 : http_status,
755 : resp);
756 4 : GNUNET_break (MHD_YES == ret);
757 4 : MHD_destroy_response (resp);
758 4 : return ret;
759 : }
760 : }
761 :
762 :
763 : /* end of taler-exchange-httpd_coins_get.c */
|