LCOV - code coverage report
Current view: top level - lib - merchant_api_get-private-products.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 0.0 % 160 0
Test Date: 2026-04-12 12:58:13 Functions: 0.0 % 6 0

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 2014-2026 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-private-products.c
      19              :  * @brief Implementation of the GET /private/products request
      20              :  * @author Christian Grothoff
      21              :  */
      22              : #include "taler/platform.h"
      23              : #include <curl/curl.h>
      24              : #include <jansson.h>
      25              : #include <microhttpd.h> /* just for HTTP status codes */
      26              : #include <gnunet/gnunet_util_lib.h>
      27              : #include <gnunet/gnunet_curl_lib.h>
      28              : #include <taler/merchant/get-private-products.h>
      29              : #include "merchant_api_curl_defaults.h"
      30              : #include <taler/taler_json_lib.h>
      31              : 
      32              : 
      33              : /**
      34              :  * Maximum number of products we return.
      35              :  */
      36              : #define MAX_PRODUCTS 1024
      37              : 
      38              : 
      39              : /**
      40              :  * Handle for a GET /private/products operation.
      41              :  */
      42              : struct TALER_MERCHANT_GetPrivateProductsHandle
      43              : {
      44              :   /**
      45              :    * Base URL of the merchant backend.
      46              :    */
      47              :   char *base_url;
      48              : 
      49              :   /**
      50              :    * The full URL for this request.
      51              :    */
      52              :   char *url;
      53              : 
      54              :   /**
      55              :    * Handle for the request.
      56              :    */
      57              :   struct GNUNET_CURL_Job *job;
      58              : 
      59              :   /**
      60              :    * Function to call with the result.
      61              :    */
      62              :   TALER_MERCHANT_GetPrivateProductsCallback cb;
      63              : 
      64              :   /**
      65              :    * Closure for @a cb.
      66              :    */
      67              :   TALER_MERCHANT_GET_PRIVATE_PRODUCTS_RESULT_CLOSURE *cb_cls;
      68              : 
      69              :   /**
      70              :    * Reference to the execution context.
      71              :    */
      72              :   struct GNUNET_CURL_Context *ctx;
      73              : 
      74              :   /**
      75              :    * Starting row for pagination.
      76              :    */
      77              :   uint64_t offset;
      78              : 
      79              :   /**
      80              :    * Limit on number of results.
      81              :    */
      82              :   int64_t limit;
      83              : 
      84              :   /**
      85              :    * Category name filter, or NULL.
      86              :    */
      87              :   char *category_filter;
      88              : 
      89              :   /**
      90              :    * Product name filter, or NULL.
      91              :    */
      92              :   char *name_filter;
      93              : 
      94              :   /**
      95              :    * Product description filter, or NULL.
      96              :    */
      97              :   char *description_filter;
      98              : 
      99              :   /**
     100              :    * Product group serial filter.
     101              :    */
     102              :   uint64_t product_group_serial;
     103              : 
     104              :   /**
     105              :    * True if offset was explicitly set.
     106              :    */
     107              :   bool have_offset;
     108              : 
     109              :   /**
     110              :    * True if product_group_serial was explicitly set.
     111              :    */
     112              :   bool have_product_group_serial;
     113              : };
     114              : 
     115              : 
     116              : /**
     117              :  * Parse product information from @a ia.
     118              :  *
     119              :  * @param ia JSON array (or NULL!) with product data
     120              :  * @param[in] pgr partially filled response
     121              :  * @param gpph operation handle
     122              :  * @return #GNUNET_OK on success
     123              :  */
     124              : static enum GNUNET_GenericReturnValue
     125            0 : parse_products (const json_t *ia,
     126              :                 struct TALER_MERCHANT_GetPrivateProductsResponse *pgr,
     127              :                 struct TALER_MERCHANT_GetPrivateProductsHandle *gpph)
     128              : {
     129            0 :   unsigned int ies_len = (unsigned int) json_array_size (ia);
     130              : 
     131            0 :   if ( (json_array_size (ia) != (size_t) ies_len) ||
     132              :        (ies_len > MAX_PRODUCTS) )
     133              :   {
     134            0 :     GNUNET_break (0);
     135            0 :     return GNUNET_SYSERR;
     136              :   }
     137            0 :   {
     138            0 :     struct TALER_MERCHANT_GetPrivateProductsInventoryEntry ies[
     139            0 :       GNUNET_NZL (ies_len)];
     140              :     size_t index;
     141              :     json_t *value;
     142              : 
     143            0 :     json_array_foreach (ia, index, value) {
     144            0 :       struct TALER_MERCHANT_GetPrivateProductsInventoryEntry *ie =
     145              :         &ies[index];
     146              :       struct GNUNET_JSON_Specification spec[] = {
     147            0 :         GNUNET_JSON_spec_string ("product_id",
     148              :                                  &ie->product_id),
     149            0 :         GNUNET_JSON_spec_uint64 ("product_serial",
     150              :                                  &ie->product_serial),
     151            0 :         GNUNET_JSON_spec_end ()
     152              :       };
     153              : 
     154            0 :       if (GNUNET_OK !=
     155            0 :           GNUNET_JSON_parse (value,
     156              :                              spec,
     157              :                              NULL, NULL))
     158              :       {
     159            0 :         GNUNET_break_op (0);
     160            0 :         return GNUNET_SYSERR;
     161              :       }
     162              :     }
     163            0 :     pgr->details.ok.products_length = ies_len;
     164            0 :     pgr->details.ok.products = ies;
     165            0 :     gpph->cb (gpph->cb_cls,
     166              :               pgr);
     167            0 :     gpph->cb = NULL;
     168              :   }
     169            0 :   return GNUNET_OK;
     170              : }
     171              : 
     172              : 
     173              : /**
     174              :  * Function called when we're done processing the
     175              :  * HTTP GET /private/products request.
     176              :  *
     177              :  * @param cls the `struct TALER_MERCHANT_GetPrivateProductsHandle`
     178              :  * @param response_code HTTP response code, 0 on error
     179              :  * @param response response body, NULL if not in JSON
     180              :  */
     181              : static void
     182            0 : handle_get_products_finished (void *cls,
     183              :                               long response_code,
     184              :                               const void *response)
     185              : {
     186            0 :   struct TALER_MERCHANT_GetPrivateProductsHandle *gpph = cls;
     187            0 :   const json_t *json = response;
     188            0 :   struct TALER_MERCHANT_GetPrivateProductsResponse pgr = {
     189            0 :     .hr.http_status = (unsigned int) response_code,
     190              :     .hr.reply = json
     191              :   };
     192              : 
     193            0 :   gpph->job = NULL;
     194            0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     195              :               "Got /private/products response with status code %u\n",
     196              :               (unsigned int) response_code);
     197            0 :   switch (response_code)
     198              :   {
     199            0 :   case MHD_HTTP_OK:
     200              :     {
     201              :       const json_t *products;
     202              :       struct GNUNET_JSON_Specification spec[] = {
     203            0 :         GNUNET_JSON_spec_array_const ("products",
     204              :                                       &products),
     205            0 :         GNUNET_JSON_spec_end ()
     206              :       };
     207              : 
     208            0 :       if (GNUNET_OK !=
     209            0 :           GNUNET_JSON_parse (json,
     210              :                              spec,
     211              :                              NULL, NULL))
     212              :       {
     213            0 :         pgr.hr.http_status = 0;
     214            0 :         pgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     215            0 :         break;
     216              :       }
     217            0 :       if (GNUNET_OK ==
     218            0 :           parse_products (products,
     219              :                           &pgr,
     220              :                           gpph))
     221              :       {
     222            0 :         TALER_MERCHANT_get_private_products_cancel (gpph);
     223            0 :         return;
     224              :       }
     225            0 :       pgr.hr.http_status = 0;
     226            0 :       pgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     227            0 :       break;
     228              :     }
     229            0 :   case MHD_HTTP_UNAUTHORIZED:
     230            0 :     pgr.hr.ec = TALER_JSON_get_error_code (json);
     231            0 :     pgr.hr.hint = TALER_JSON_get_error_hint (json);
     232            0 :     break;
     233            0 :   default:
     234            0 :     pgr.hr.ec = TALER_JSON_get_error_code (json);
     235            0 :     pgr.hr.hint = TALER_JSON_get_error_hint (json);
     236            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     237              :                 "Unexpected response code %u/%d\n",
     238              :                 (unsigned int) response_code,
     239              :                 (int) pgr.hr.ec);
     240            0 :     break;
     241              :   }
     242            0 :   gpph->cb (gpph->cb_cls,
     243              :             &pgr);
     244            0 :   TALER_MERCHANT_get_private_products_cancel (gpph);
     245              : }
     246              : 
     247              : 
     248              : struct TALER_MERCHANT_GetPrivateProductsHandle *
     249            0 : TALER_MERCHANT_get_private_products_create (
     250              :   struct GNUNET_CURL_Context *ctx,
     251              :   const char *url)
     252              : {
     253              :   struct TALER_MERCHANT_GetPrivateProductsHandle *gpph;
     254              : 
     255            0 :   gpph = GNUNET_new (struct TALER_MERCHANT_GetPrivateProductsHandle);
     256            0 :   gpph->ctx = ctx;
     257            0 :   gpph->base_url = GNUNET_strdup (url);
     258            0 :   gpph->limit = 20;
     259            0 :   gpph->offset = INT64_MAX;
     260            0 :   return gpph;
     261              : }
     262              : 
     263              : 
     264              : enum GNUNET_GenericReturnValue
     265            0 : TALER_MERCHANT_get_private_products_set_options_ (
     266              :   struct TALER_MERCHANT_GetPrivateProductsHandle *gpph,
     267              :   unsigned int num_options,
     268              :   const struct TALER_MERCHANT_GetPrivateProductsOptionValue *options)
     269              : {
     270            0 :   for (unsigned int i = 0; i < num_options; i++)
     271              :   {
     272            0 :     const struct TALER_MERCHANT_GetPrivateProductsOptionValue *opt =
     273            0 :       &options[i];
     274              : 
     275            0 :     switch (opt->option)
     276              :     {
     277            0 :     case TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_END:
     278            0 :       return GNUNET_OK;
     279            0 :     case TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_LIMIT:
     280            0 :       gpph->limit = opt->details.limit;
     281            0 :       break;
     282            0 :     case TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_OFFSET:
     283            0 :       if (opt->details.offset > INT64_MAX)
     284              :       {
     285            0 :         GNUNET_break (0);
     286            0 :         return GNUNET_NO;
     287              :       }
     288            0 :       gpph->offset = opt->details.offset;
     289            0 :       gpph->have_offset = true;
     290            0 :       break;
     291            0 :     case TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_CATEGORY_FILTER:
     292            0 :       GNUNET_free (gpph->category_filter);
     293            0 :       if (NULL != opt->details.category_filter)
     294            0 :         gpph->category_filter = GNUNET_strdup (opt->details.category_filter);
     295            0 :       break;
     296            0 :     case TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_NAME_FILTER:
     297            0 :       GNUNET_free (gpph->name_filter);
     298            0 :       if (NULL != opt->details.name_filter)
     299            0 :         gpph->name_filter = GNUNET_strdup (opt->details.name_filter);
     300            0 :       break;
     301            0 :     case TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_DESCRIPTION_FILTER:
     302            0 :       GNUNET_free (gpph->description_filter);
     303            0 :       if (NULL != opt->details.description_filter)
     304            0 :         gpph->description_filter =
     305            0 :           GNUNET_strdup (opt->details.description_filter);
     306            0 :       break;
     307            0 :     case TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_PRODUCT_GROUP_SERIAL:
     308            0 :       gpph->product_group_serial = opt->details.product_group_serial;
     309            0 :       gpph->have_product_group_serial = true;
     310            0 :       break;
     311            0 :     default:
     312            0 :       GNUNET_break (0);
     313            0 :       return GNUNET_NO;
     314              :     }
     315              :   }
     316            0 :   return GNUNET_OK;
     317              : }
     318              : 
     319              : 
     320              : enum TALER_ErrorCode
     321            0 : TALER_MERCHANT_get_private_products_start (
     322              :   struct TALER_MERCHANT_GetPrivateProductsHandle *gpph,
     323              :   TALER_MERCHANT_GetPrivateProductsCallback cb,
     324              :   TALER_MERCHANT_GET_PRIVATE_PRODUCTS_RESULT_CLOSURE *cb_cls)
     325              : {
     326              :   CURL *eh;
     327              : 
     328            0 :   gpph->cb = cb;
     329            0 :   gpph->cb_cls = cb_cls;
     330              :   {
     331            0 :     char *cfilt = NULL;
     332            0 :     char *nfilt = NULL;
     333            0 :     char *dfilt = NULL;
     334              :     char lbuf[30];
     335              :     char obuf[30];
     336              :     char pgsbuf[30];
     337              :     bool have_offset;
     338              : 
     339            0 :     GNUNET_snprintf (lbuf,
     340              :                      sizeof (lbuf),
     341              :                      "%lld",
     342            0 :                      (long long) gpph->limit);
     343            0 :     GNUNET_snprintf (obuf,
     344              :                      sizeof (obuf),
     345              :                      "%llu",
     346            0 :                      (unsigned long long) gpph->offset);
     347            0 :     if (gpph->have_product_group_serial)
     348            0 :       GNUNET_snprintf (pgsbuf,
     349              :                        sizeof (pgsbuf),
     350              :                        "%llu",
     351            0 :                        (unsigned long long) gpph->product_group_serial);
     352            0 :     if (gpph->limit > 0)
     353              :     {
     354            0 :       have_offset = gpph->have_offset
     355            0 :                     && (0 != gpph->offset);
     356              :     }
     357              :     else
     358              :     {
     359            0 :       have_offset = gpph->have_offset
     360            0 :                     && (INT64_MAX != gpph->offset);
     361              :     }
     362            0 :     if (NULL != gpph->category_filter)
     363            0 :       (void) GNUNET_STRINGS_urlencode (strlen (gpph->category_filter),
     364            0 :                                        gpph->category_filter,
     365              :                                        &cfilt);
     366            0 :     if (NULL != gpph->name_filter)
     367            0 :       (void) GNUNET_STRINGS_urlencode (strlen (gpph->name_filter),
     368            0 :                                        gpph->name_filter,
     369              :                                        &nfilt);
     370            0 :     if (NULL != gpph->description_filter)
     371            0 :       (void) GNUNET_STRINGS_urlencode (strlen (gpph->description_filter),
     372            0 :                                        gpph->description_filter,
     373              :                                        &dfilt);
     374            0 :     gpph->url = TALER_url_join (gpph->base_url,
     375              :                                 "private/products",
     376              :                                 "limit",
     377            0 :                                 (20 != gpph->limit)
     378              :                                 ? lbuf
     379              :                                 : NULL,
     380              :                                 "offset",
     381              :                                 have_offset
     382              :                                 ? obuf
     383              :                                 : NULL,
     384              :                                 "category_filter",
     385              :                                 cfilt,
     386              :                                 "name_filter",
     387              :                                 nfilt,
     388              :                                 "description_filter",
     389              :                                 dfilt,
     390              :                                 "product_group_serial",
     391            0 :                                 gpph->have_product_group_serial
     392              :                                 ? pgsbuf
     393              :                                 : NULL,
     394              :                                 NULL);
     395            0 :     GNUNET_free (cfilt);
     396            0 :     GNUNET_free (nfilt);
     397            0 :     GNUNET_free (dfilt);
     398              :   }
     399            0 :   if (NULL == gpph->url)
     400              :   {
     401            0 :     GNUNET_break (0);
     402            0 :     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
     403              :   }
     404            0 :   eh = TALER_MERCHANT_curl_easy_get_ (gpph->url);
     405            0 :   if (NULL == eh)
     406              :   {
     407            0 :     GNUNET_break (0);
     408            0 :     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
     409              :   }
     410            0 :   gpph->job = GNUNET_CURL_job_add (gpph->ctx,
     411              :                                    eh,
     412              :                                    &handle_get_products_finished,
     413              :                                    gpph);
     414            0 :   if (NULL == gpph->job)
     415              :   {
     416            0 :     GNUNET_break (0);
     417            0 :     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
     418              :   }
     419            0 :   return TALER_EC_NONE;
     420              : }
     421              : 
     422              : 
     423              : void
     424            0 : TALER_MERCHANT_get_private_products_cancel (
     425              :   struct TALER_MERCHANT_GetPrivateProductsHandle *gpph)
     426              : {
     427            0 :   if (NULL != gpph->job)
     428              :   {
     429            0 :     GNUNET_CURL_job_cancel (gpph->job);
     430            0 :     gpph->job = NULL;
     431              :   }
     432            0 :   GNUNET_free (gpph->url);
     433            0 :   GNUNET_free (gpph->category_filter);
     434            0 :   GNUNET_free (gpph->name_filter);
     435            0 :   GNUNET_free (gpph->description_filter);
     436            0 :   GNUNET_free (gpph->base_url);
     437            0 :   GNUNET_free (gpph);
     438            0 : }
     439              : 
     440              : 
     441              : /* end of merchant_api_get-private-products.c */
        

Generated by: LCOV version 2.0-1