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           12 :     json_array_foreach (jintervals, index, value) {
     409            8 :       struct TALER_MERCHANT_StatisticCounterByInterval *jinterval
     410              :         = &resp_intervals[index];
     411              :       struct GNUNET_JSON_Specification spec[] = {
     412            8 :         GNUNET_JSON_spec_timestamp ("start_time",
     413              :                                     &jinterval->start_time),
     414            8 :         GNUNET_JSON_spec_uint64 ("cumulative_counter",
     415              :                                  &jinterval->cumulative_counter),
     416            8 :         GNUNET_JSON_spec_end ()
     417              :       };
     418              : 
     419            8 :       if (GNUNET_OK !=
     420            8 :           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            8 :       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 : }
        
               |