Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2025 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify it under the
6 : terms of the GNU Lesser General Public License as published by the Free Software
7 : Foundation; either version 2.1, 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 Lesser General Public License for more details.
12 :
13 : You should have received a copy of the GNU Lesser General Public License along with
14 : TALER; see the file COPYING.LGPL. If not, see
15 : <http://www.gnu.org/licenses/>
16 : */
17 : /**
18 : * @file merchant_api_get_statistics.c
19 : * @brief Implementation of the GET /statistics-[counter,amount]/$SLUG request of the merchant's HTTP API
20 : * @author Martin Schanzenbach
21 : */
22 : #include "platform.h"
23 : #include <curl/curl.h>
24 : #include <gnunet/gnunet_common.h>
25 : #include <gnunet/gnunet_json_lib.h>
26 : #include <jansson.h>
27 : #include <microhttpd.h> /* just for HTTP status codes */
28 : #include <gnunet/gnunet_util_lib.h>
29 : #include <gnunet/gnunet_curl_lib.h>
30 : #include "taler_merchant_service.h"
31 : #include "merchant_api_curl_defaults.h"
32 : #include <taler/taler_json_lib.h>
33 : #include <taler/taler_signatures.h>
34 :
35 : /**
36 : * Maximum number of statistics we return
37 : */
38 : #define MAX_STATISTICS 1024
39 :
40 : /**
41 : * Handle for a GET /statistics-amount/$SLUG operation.
42 : */
43 : struct TALER_MERCHANT_StatisticsAmountGetHandle
44 : {
45 : /**
46 : * The url for this request.
47 : */
48 : char *url;
49 :
50 : /**
51 : * Handle for the request.
52 : */
53 : struct GNUNET_CURL_Job *job;
54 :
55 : /**
56 : * Function to call with the result.
57 : */
58 : TALER_MERCHANT_StatisticsAmountGetCallback cb;
59 :
60 : /**
61 : * Closure for @a cb.
62 : */
63 : void *cb_cls;
64 :
65 : /**
66 : * Reference to the execution context.
67 : */
68 : struct GNUNET_CURL_Context *ctx;
69 :
70 : };
71 :
72 : /**
73 : * Handle for a GET /statistics-counter/$SLUG operation.
74 : */
75 : struct TALER_MERCHANT_StatisticsCounterGetHandle
76 : {
77 : /**
78 : * The url for this request.
79 : */
80 : char *url;
81 :
82 : /**
83 : * Handle for the request.
84 : */
85 : struct GNUNET_CURL_Job *job;
86 :
87 : /**
88 : * Function to call with the result.
89 : */
90 : TALER_MERCHANT_StatisticsCounterGetCallback cb;
91 :
92 : /**
93 : * Closure for @a cb.
94 : */
95 : void *cb_cls;
96 :
97 : /**
98 : * Reference to the execution context.
99 : */
100 : struct GNUNET_CURL_Context *ctx;
101 :
102 : };
103 :
104 :
105 : /**
106 : * Parse interval information from buckets and intervals.
107 : *
108 : * @param json overall JSON reply
109 : * @param jbuckets JSON array (or NULL!) with bucket data
110 : * @param buckets_description human-readable description for the buckets
111 : * @param jintervals JSON array (or NULL!) with bucket data
112 : * @param intervals_description human-readable description for the intervals
113 : * @param sgh operation handle
114 : * @return #GNUNET_OK on success
115 : */
116 : static enum GNUNET_GenericReturnValue
117 2 : parse_intervals_and_buckets_amt (
118 : const json_t *json,
119 : const json_t *jbuckets,
120 : const char *buckets_description,
121 : const json_t *jintervals,
122 : const char *intervals_description,
123 : struct TALER_MERCHANT_StatisticsAmountGetHandle *sgh
124 : )
125 : {
126 2 : unsigned int resp_buckets_len = json_array_size (jbuckets);
127 2 : unsigned int resp_intervals_len = json_array_size (jintervals);
128 :
129 2 : if ( (json_array_size (jbuckets) != (size_t) resp_buckets_len) ||
130 2 : (json_array_size (jintervals) != (size_t) resp_intervals_len) ||
131 2 : (resp_intervals_len = resp_buckets_len > MAX_STATISTICS) )
132 : {
133 0 : GNUNET_break (0);
134 0 : return GNUNET_SYSERR;
135 : }
136 2 : {
137 2 : struct TALER_MERCHANT_StatisticAmountByBucket resp_buckets[
138 2 : GNUNET_NZL (resp_buckets_len)];
139 2 : struct TALER_MERCHANT_StatisticAmountByInterval resp_intervals[
140 2 : GNUNET_NZL (resp_intervals_len)];
141 : size_t index;
142 : json_t *value;
143 : enum GNUNET_GenericReturnValue ret;
144 :
145 2 : ret = GNUNET_OK;
146 2 : json_array_foreach (jintervals, index, value) {
147 0 : struct TALER_MERCHANT_StatisticAmountByInterval *jinterval
148 : = &resp_intervals[index];
149 : const json_t *amounts_arr;
150 : size_t amounts_len;
151 :
152 : struct GNUNET_JSON_Specification spec[] = {
153 0 : GNUNET_JSON_spec_timestamp ("start_time",
154 : &jinterval->start_time),
155 0 : GNUNET_JSON_spec_array_const ("cumulative_amount",
156 : &amounts_arr),
157 0 : GNUNET_JSON_spec_end ()
158 : };
159 :
160 0 : if (GNUNET_OK !=
161 0 : GNUNET_JSON_parse (value,
162 : spec,
163 : NULL, NULL))
164 : {
165 0 : GNUNET_break_op (0);
166 0 : ret = GNUNET_SYSERR;
167 0 : continue;
168 : }
169 0 : if (GNUNET_SYSERR == ret)
170 0 : break;
171 0 : amounts_len = json_array_size (amounts_arr);
172 0 : {
173 0 : struct TALER_Amount amt_arr[amounts_len];
174 : size_t aindex;
175 : json_t *avalue;
176 :
177 0 : jinterval->cumulative_amount_len = amounts_len;
178 0 : jinterval->cumulative_amounts = amt_arr;
179 0 : json_array_foreach (amounts_arr, aindex, avalue) {
180 0 : if (! json_is_string (avalue))
181 : {
182 0 : GNUNET_break_op (0);
183 0 : return GNUNET_SYSERR;
184 : }
185 0 : if (GNUNET_OK !=
186 0 : TALER_string_to_amount (json_string_value (avalue),
187 : &amt_arr[aindex]))
188 : {
189 0 : GNUNET_break_op (0);
190 0 : return GNUNET_SYSERR;
191 : }
192 : }
193 : }
194 : }
195 2 : ret = GNUNET_OK;
196 14 : json_array_foreach (jbuckets, index, value) {
197 12 : struct TALER_MERCHANT_StatisticAmountByBucket *jbucket
198 : = &resp_buckets[index];
199 : const json_t *amounts_arr;
200 : size_t amounts_len;
201 : struct GNUNET_JSON_Specification spec[] = {
202 12 : GNUNET_JSON_spec_timestamp ("start_time",
203 : &jbucket->start_time),
204 12 : GNUNET_JSON_spec_timestamp ("end_time",
205 : &jbucket->end_time),
206 12 : GNUNET_JSON_spec_string ("range",
207 : &jbucket->range),
208 12 : GNUNET_JSON_spec_array_const ("cumulative_amount",
209 : &amounts_arr),
210 12 : GNUNET_JSON_spec_end ()
211 : };
212 :
213 12 : if (GNUNET_OK !=
214 12 : GNUNET_JSON_parse (value,
215 : spec,
216 : NULL, NULL))
217 : {
218 0 : GNUNET_break_op (0);
219 0 : ret = GNUNET_SYSERR;
220 0 : continue;
221 : }
222 12 : if (GNUNET_SYSERR == ret)
223 0 : break;
224 12 : amounts_len = json_array_size (amounts_arr);
225 : if (0 > amounts_len)
226 : {
227 : GNUNET_break_op (0);
228 : ret = GNUNET_SYSERR;
229 : break;
230 : }
231 12 : {
232 12 : struct TALER_Amount amt_arr[amounts_len];
233 : size_t aindex;
234 : json_t *avalue;
235 12 : jbucket->cumulative_amount_len = amounts_len;
236 12 : jbucket->cumulative_amounts = amt_arr;
237 24 : json_array_foreach (amounts_arr, aindex, avalue) {
238 12 : if (! json_is_string (avalue))
239 : {
240 0 : GNUNET_break_op (0);
241 0 : return GNUNET_SYSERR;
242 : }
243 12 : if (GNUNET_OK !=
244 12 : TALER_string_to_amount (json_string_value (avalue),
245 : &amt_arr[aindex]))
246 : {
247 0 : GNUNET_break_op (0);
248 0 : return GNUNET_SYSERR;
249 : }
250 : }
251 : }
252 : }
253 2 : if (GNUNET_OK == ret)
254 : {
255 2 : struct TALER_MERCHANT_StatisticsAmountGetResponse gsr = {
256 : .hr.http_status = MHD_HTTP_OK,
257 : .hr.reply = json,
258 : .details.ok.buckets_length = resp_buckets_len,
259 : .details.ok.buckets = resp_buckets,
260 : .details.ok.buckets_description = buckets_description,
261 : .details.ok.intervals_length = resp_intervals_len,
262 : .details.ok.intervals = resp_intervals,
263 : .details.ok.intervals_description = intervals_description,
264 : };
265 2 : sgh->cb (sgh->cb_cls,
266 : &gsr);
267 2 : sgh->cb = NULL; /* just to be sure */
268 : }
269 2 : return ret;
270 : }
271 : }
272 :
273 :
274 : /**
275 : * Function called when we're done processing the
276 : * HTTP GET /statistics-amount/$SLUG request.
277 : *
278 : * @param cls the `struct TALER_MERCHANT_StatisticsAmountGetHandle`
279 : * @param response_code HTTP response code, 0 on error
280 : * @param response response body, NULL if not in JSON
281 : */
282 : static void
283 2 : handle_get_statistics_amount_finished (void *cls,
284 : long response_code,
285 : const void *response)
286 : {
287 2 : struct TALER_MERCHANT_StatisticsAmountGetHandle *handle = cls;
288 2 : const json_t *json = response;
289 2 : struct TALER_MERCHANT_StatisticsAmountGetResponse res = {
290 2 : .hr.http_status = (unsigned int) response_code,
291 : .hr.reply = json
292 : };
293 :
294 2 : handle->job = NULL;
295 2 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
296 : "Got /statistics-amount/$SLUG response with status code %u\n",
297 : (unsigned int) response_code);
298 2 : switch (response_code)
299 : {
300 2 : case MHD_HTTP_OK:
301 : {
302 : const json_t *buckets;
303 : const json_t *intervals;
304 2 : const char *buckets_description = NULL;
305 2 : const char *intervals_description = NULL;
306 : struct GNUNET_JSON_Specification spec[] = {
307 2 : GNUNET_JSON_spec_array_const ("buckets",
308 : &buckets),
309 2 : GNUNET_JSON_spec_mark_optional (
310 : GNUNET_JSON_spec_string ("buckets_description",
311 : &buckets_description),
312 : NULL),
313 2 : GNUNET_JSON_spec_array_const ("intervals",
314 : &intervals),
315 2 : GNUNET_JSON_spec_mark_optional (
316 : GNUNET_JSON_spec_string ("intervals_description",
317 : &intervals_description),
318 : NULL),
319 2 : GNUNET_JSON_spec_end ()
320 : };
321 :
322 2 : if (GNUNET_OK !=
323 2 : GNUNET_JSON_parse (json,
324 : spec,
325 : NULL, NULL))
326 : {
327 0 : res.hr.http_status = 0;
328 0 : res.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
329 0 : break;
330 : }
331 2 : if (GNUNET_OK ==
332 2 : parse_intervals_and_buckets_amt (json,
333 : buckets,
334 : buckets_description,
335 : intervals,
336 : intervals_description,
337 : handle))
338 : {
339 2 : TALER_MERCHANT_statistic_amount_get_cancel (handle);
340 2 : return;
341 : }
342 0 : res.hr.http_status = 0;
343 0 : res.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
344 0 : break;
345 : }
346 0 : case MHD_HTTP_UNAUTHORIZED:
347 0 : res.hr.ec = TALER_JSON_get_error_code (json);
348 0 : res.hr.hint = TALER_JSON_get_error_hint (json);
349 : /* Nothing really to verify, merchant says we need to authenticate. */
350 0 : break;
351 0 : case MHD_HTTP_NOT_FOUND:
352 0 : res.hr.ec = TALER_JSON_get_error_code (json);
353 0 : res.hr.hint = TALER_JSON_get_error_hint (json);
354 0 : break;
355 0 : default:
356 : /* unexpected response code */
357 0 : res.hr.ec = TALER_JSON_get_error_code (json);
358 0 : res.hr.hint = TALER_JSON_get_error_hint (json);
359 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
360 : "Unexpected response code %u/%d\n",
361 : (unsigned int) response_code,
362 : (int) res.hr.ec);
363 0 : break;
364 : }
365 : }
366 :
367 :
368 : /**
369 : * Parse interval information from @a ia.
370 : *
371 : * @param json overall JSON reply
372 : * @param jbuckets JSON array (or NULL!) with bucket data
373 : * @param buckets_description human-readable description for the buckets
374 : * @param jintervals JSON array (or NULL!) with bucket data
375 : * @param intervals_description human-readable description for the intervals
376 : * @param scgh operation handle
377 : * @return #GNUNET_OK on success
378 : */
379 : static enum GNUNET_GenericReturnValue
380 4 : parse_intervals_and_buckets (
381 : const json_t *json,
382 : const json_t *jbuckets,
383 : const char *buckets_description,
384 : const json_t *jintervals,
385 : const char *intervals_description,
386 : struct TALER_MERCHANT_StatisticsCounterGetHandle *scgh)
387 : {
388 4 : unsigned int resp_buckets_len = json_array_size (jbuckets);
389 4 : unsigned int resp_intervals_len = json_array_size (jintervals);
390 :
391 4 : if ( (json_array_size (jbuckets) != (size_t) resp_buckets_len) ||
392 4 : (json_array_size (jintervals) != (size_t) resp_intervals_len) ||
393 4 : (resp_intervals_len = resp_buckets_len > MAX_STATISTICS) )
394 : {
395 0 : GNUNET_break (0);
396 0 : return GNUNET_SYSERR;
397 : }
398 4 : {
399 4 : struct TALER_MERCHANT_StatisticCounterByBucket resp_buckets[
400 4 : GNUNET_NZL (resp_buckets_len)];
401 4 : struct TALER_MERCHANT_StatisticCounterByInterval resp_intervals[
402 4 : GNUNET_NZL (resp_intervals_len)];
403 : size_t index;
404 : json_t *value;
405 : enum GNUNET_GenericReturnValue ret;
406 :
407 4 : ret = GNUNET_OK;
408 8 : json_array_foreach (jintervals, index, value) {
409 4 : struct TALER_MERCHANT_StatisticCounterByInterval *jinterval
410 : = &resp_intervals[index];
411 : struct GNUNET_JSON_Specification spec[] = {
412 4 : GNUNET_JSON_spec_timestamp ("start_time",
413 : &jinterval->start_time),
414 4 : GNUNET_JSON_spec_uint64 ("cumulative_counter",
415 : &jinterval->cumulative_counter),
416 4 : GNUNET_JSON_spec_end ()
417 : };
418 :
419 4 : if (GNUNET_OK !=
420 4 : GNUNET_JSON_parse (value,
421 : spec,
422 : NULL, NULL))
423 : {
424 0 : GNUNET_break_op (0);
425 0 : ret = GNUNET_SYSERR;
426 0 : continue;
427 : }
428 4 : if (GNUNET_SYSERR == ret)
429 0 : break;
430 : }
431 4 : ret = GNUNET_OK;
432 28 : json_array_foreach (jbuckets, index, value) {
433 24 : struct TALER_MERCHANT_StatisticCounterByBucket *jbucket = &resp_buckets[
434 : index];
435 : struct GNUNET_JSON_Specification spec[] = {
436 24 : GNUNET_JSON_spec_timestamp ("start_time",
437 : &jbucket->start_time),
438 24 : GNUNET_JSON_spec_timestamp ("end_time",
439 : &jbucket->end_time),
440 24 : GNUNET_JSON_spec_string ("range",
441 : &jbucket->range),
442 24 : GNUNET_JSON_spec_uint64 ("cumulative_counter",
443 : &jbucket->cumulative_counter),
444 24 : GNUNET_JSON_spec_end ()
445 : };
446 :
447 24 : if (GNUNET_OK !=
448 24 : GNUNET_JSON_parse (value,
449 : spec,
450 : NULL, NULL))
451 : {
452 0 : GNUNET_break_op (0);
453 0 : ret = GNUNET_SYSERR;
454 0 : continue;
455 : }
456 24 : if (GNUNET_SYSERR == ret)
457 0 : break;
458 : }
459 4 : if (GNUNET_OK == ret)
460 : {
461 4 : struct TALER_MERCHANT_StatisticsCounterGetResponse gsr = {
462 : .hr.http_status = MHD_HTTP_OK,
463 : .hr.reply = json,
464 : .details.ok.buckets_length = resp_buckets_len,
465 : .details.ok.buckets = resp_buckets,
466 : .details.ok.buckets_description = buckets_description,
467 : .details.ok.intervals_length = resp_intervals_len,
468 : .details.ok.intervals = resp_intervals,
469 : .details.ok.intervals_description = intervals_description,
470 : };
471 4 : scgh->cb (scgh->cb_cls,
472 : &gsr);
473 4 : scgh->cb = NULL; /* just to be sure */
474 : }
475 4 : return ret;
476 : }
477 : }
478 :
479 :
480 : /**
481 : * Function called when we're done processing the
482 : * HTTP GET /statistics-counter/$SLUG request.
483 : *
484 : * @param cls the `struct TALER_MERCHANT_StatisticsCounterGetHandle`
485 : * @param response_code HTTP response code, 0 on error
486 : * @param response response body, NULL if not in JSON
487 : */
488 : static void
489 4 : handle_get_statistics_counter_finished (void *cls,
490 : long response_code,
491 : const void *response)
492 : {
493 4 : struct TALER_MERCHANT_StatisticsCounterGetHandle *handle = cls;
494 4 : const json_t *json = response;
495 4 : struct TALER_MERCHANT_StatisticsCounterGetResponse res = {
496 4 : .hr.http_status = (unsigned int) response_code,
497 : .hr.reply = json
498 : };
499 :
500 4 : handle->job = NULL;
501 4 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
502 : "Got /statistics-counter/$SLUG response with status code %u\n",
503 : (unsigned int) response_code);
504 4 : switch (response_code)
505 : {
506 4 : case MHD_HTTP_OK:
507 : {
508 : const json_t *buckets;
509 : const json_t *intervals;
510 : const char *buckets_description;
511 : const char *intervals_description;
512 : struct GNUNET_JSON_Specification spec[] = {
513 4 : GNUNET_JSON_spec_array_const ("buckets",
514 : &buckets),
515 4 : GNUNET_JSON_spec_mark_optional (
516 : GNUNET_JSON_spec_string ("buckets_description",
517 : &buckets_description),
518 : NULL),
519 4 : GNUNET_JSON_spec_array_const ("intervals",
520 : &intervals),
521 4 : GNUNET_JSON_spec_mark_optional (
522 : GNUNET_JSON_spec_string ("intervals_description",
523 : &intervals_description),
524 : NULL),
525 4 : GNUNET_JSON_spec_end ()
526 : };
527 :
528 4 : if (GNUNET_OK !=
529 4 : GNUNET_JSON_parse (json,
530 : spec,
531 : NULL, NULL))
532 : {
533 0 : res.hr.http_status = 0;
534 0 : res.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
535 0 : break;
536 : }
537 4 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
538 : "%s\n", json_dumps (json, JSON_INDENT (1)));
539 4 : if (GNUNET_OK ==
540 4 : parse_intervals_and_buckets (json,
541 : buckets,
542 : buckets_description,
543 : intervals,
544 : intervals_description,
545 : handle))
546 : {
547 4 : TALER_MERCHANT_statistic_counter_get_cancel (handle);
548 4 : return;
549 : }
550 0 : res.hr.http_status = 0;
551 0 : res.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
552 0 : break;
553 : }
554 0 : case MHD_HTTP_UNAUTHORIZED:
555 0 : res.hr.ec = TALER_JSON_get_error_code (json);
556 0 : res.hr.hint = TALER_JSON_get_error_hint (json);
557 : /* Nothing really to verify, merchant says we need to authenticate. */
558 0 : break;
559 0 : case MHD_HTTP_NOT_FOUND:
560 0 : res.hr.ec = TALER_JSON_get_error_code (json);
561 0 : res.hr.hint = TALER_JSON_get_error_hint (json);
562 0 : break;
563 0 : default:
564 : /* unexpected response code */
565 0 : res.hr.ec = TALER_JSON_get_error_code (json);
566 0 : res.hr.hint = TALER_JSON_get_error_hint (json);
567 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
568 : "Unexpected response code %u/%d\n",
569 : (unsigned int) response_code,
570 : (int) res.hr.ec);
571 0 : break;
572 : }
573 : }
574 :
575 :
576 : struct TALER_MERCHANT_StatisticsCounterGetHandle *
577 4 : TALER_MERCHANT_statistic_counter_get (
578 : struct GNUNET_CURL_Context *ctx,
579 : const char *backend_url,
580 : const char *slug,
581 : enum TALER_MERCHANT_StatisticsType stype,
582 : TALER_MERCHANT_StatisticsCounterGetCallback cb,
583 : void *cb_cls)
584 : {
585 : struct TALER_MERCHANT_StatisticsCounterGetHandle *handle;
586 : CURL *eh;
587 :
588 4 : handle = GNUNET_new (struct TALER_MERCHANT_StatisticsCounterGetHandle);
589 4 : handle->ctx = ctx;
590 4 : handle->cb = cb;
591 4 : handle->cb_cls = cb_cls;
592 : {
593 4 : const char *filter = NULL;
594 : char *path;
595 :
596 4 : switch (stype)
597 : {
598 0 : case TALER_MERCHANT_STATISTICS_BY_BUCKET:
599 0 : filter = "bucket";
600 0 : break;
601 0 : case TALER_MERCHANT_STATISTICS_BY_INTERVAL:
602 0 : filter = "interval";
603 0 : break;
604 4 : case TALER_MERCHANT_STATISTICS_ALL:
605 4 : filter = NULL;
606 4 : break;
607 : }
608 4 : GNUNET_asprintf (&path,
609 : "private/statistics-counter/%s",
610 : slug);
611 4 : handle->url = TALER_url_join (backend_url,
612 : path,
613 : "by",
614 : filter,
615 : NULL);
616 4 : GNUNET_free (path);
617 : }
618 4 : if (NULL == handle->url)
619 : {
620 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
621 : "Could not construct request URL.\n");
622 0 : GNUNET_free (handle);
623 0 : return NULL;
624 : }
625 4 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
626 : "Requesting URL '%s'\n",
627 : handle->url);
628 4 : eh = TALER_MERCHANT_curl_easy_get_ (handle->url);
629 4 : handle->job = GNUNET_CURL_job_add (ctx,
630 : eh,
631 : &handle_get_statistics_counter_finished,
632 : handle);
633 4 : return handle;
634 : }
635 :
636 :
637 : void
638 4 : TALER_MERCHANT_statistic_counter_get_cancel (
639 : struct TALER_MERCHANT_StatisticsCounterGetHandle *handle)
640 : {
641 4 : if (NULL != handle->job)
642 0 : GNUNET_CURL_job_cancel (handle->job);
643 4 : GNUNET_free (handle->url);
644 4 : GNUNET_free (handle);
645 4 : }
646 :
647 :
648 : struct TALER_MERCHANT_StatisticsAmountGetHandle *
649 2 : TALER_MERCHANT_statistic_amount_get (
650 : struct GNUNET_CURL_Context *ctx,
651 : const char *backend_url,
652 : const char *slug,
653 : enum TALER_MERCHANT_StatisticsType stype,
654 : TALER_MERCHANT_StatisticsAmountGetCallback cb,
655 : void *cb_cls)
656 : {
657 : struct TALER_MERCHANT_StatisticsAmountGetHandle *handle;
658 : CURL *eh;
659 :
660 2 : handle = GNUNET_new (struct TALER_MERCHANT_StatisticsAmountGetHandle);
661 2 : handle->ctx = ctx;
662 2 : handle->cb = cb;
663 2 : handle->cb_cls = cb_cls;
664 : {
665 2 : const char *filter = NULL;
666 : char *path;
667 :
668 2 : switch (stype)
669 : {
670 0 : case TALER_MERCHANT_STATISTICS_BY_BUCKET:
671 0 : filter = "bucket";
672 0 : break;
673 0 : case TALER_MERCHANT_STATISTICS_BY_INTERVAL:
674 0 : filter = "interval";
675 0 : break;
676 2 : case TALER_MERCHANT_STATISTICS_ALL:
677 2 : filter = NULL;
678 2 : break;
679 : }
680 2 : GNUNET_asprintf (&path,
681 : "private/statistics-amount/%s",
682 : slug);
683 2 : handle->url = TALER_url_join (backend_url,
684 : path,
685 : "by",
686 : filter,
687 : NULL);
688 2 : GNUNET_free (path);
689 : }
690 2 : if (NULL == handle->url)
691 : {
692 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
693 : "Could not construct request URL.\n");
694 0 : GNUNET_free (handle);
695 0 : return NULL;
696 : }
697 2 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
698 : "Requesting URL '%s'\n",
699 : handle->url);
700 2 : eh = TALER_MERCHANT_curl_easy_get_ (handle->url);
701 2 : handle->job = GNUNET_CURL_job_add (ctx,
702 : eh,
703 : &handle_get_statistics_amount_finished,
704 : handle);
705 2 : return handle;
706 : }
707 :
708 :
709 : void
710 2 : TALER_MERCHANT_statistic_amount_get_cancel (
711 : struct TALER_MERCHANT_StatisticsAmountGetHandle *handle)
712 : {
713 2 : if (NULL != handle->job)
714 0 : GNUNET_CURL_job_cancel (handle->job);
715 2 : GNUNET_free (handle->url);
716 2 : GNUNET_free (handle);
717 2 : }
|