LCOV - code coverage report
Current view: top level - lib - merchant_api_get-private-products-PRODUCT_ID.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 79.0 % 105 83
Test Date: 2026-09-11 17:45:24 Functions: 100.0 % 4 4

            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 src/lib/merchant_api_get-private-products-PRODUCT_ID.c
      19              :  * @brief Implementation of the GET /private/products/$PRODUCT_ID request
      20              :  * @author Christian Grothoff
      21              :  */
      22              : #include "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-PRODUCT_ID.h>
      29              : #include "merchant_api_curl_defaults.h"
      30              : #include <taler/taler_json_lib.h>
      31              : 
      32              : 
      33              : #define MAX_CATEGORIES 1024
      34              : 
      35              : /**
      36              :  * Handle for a GET /private/products/$PRODUCT_ID operation.
      37              :  */
      38              : struct TALER_MERCHANT_GetPrivateProductHandle
      39              : {
      40              :   /**
      41              :    * Base URL of the merchant backend.
      42              :    */
      43              :   char *base_url;
      44              : 
      45              :   /**
      46              :    * Product identifier.
      47              :    */
      48              :   char *product_id;
      49              : 
      50              :   /**
      51              :    * The full URL for this request.
      52              :    */
      53              :   char *url;
      54              : 
      55              :   /**
      56              :    * Handle for the request.
      57              :    */
      58              :   struct GNUNET_CURL_Job *job;
      59              : 
      60              :   /**
      61              :    * Function to call with the result.
      62              :    */
      63              :   TALER_MERCHANT_GetPrivateProductCallback cb;
      64              : 
      65              :   /**
      66              :    * Closure for @a cb.
      67              :    */
      68              :   TALER_MERCHANT_GET_PRIVATE_PRODUCT_RESULT_CLOSURE *cb_cls;
      69              : 
      70              :   /**
      71              :    * Reference to the execution context.
      72              :    */
      73              :   struct GNUNET_CURL_Context *ctx;
      74              : };
      75              : 
      76              : 
      77              : /**
      78              :  * Function called when we're done processing the
      79              :  * HTTP GET /private/products/$PRODUCT_ID request.
      80              :  *
      81              :  * @param cls the `struct TALER_MERCHANT_GetPrivateProductHandle`
      82              :  * @param response_code HTTP response code, 0 on error
      83              :  * @param response response body, NULL if not in JSON
      84              :  */
      85              : static void
      86           32 : handle_get_product_finished (void *cls,
      87              :                              long response_code,
      88              :                              const void *response)
      89              : {
      90           32 :   struct TALER_MERCHANT_GetPrivateProductHandle *gpp = cls;
      91           32 :   const json_t *json = response;
      92           32 :   struct TALER_MERCHANT_GetPrivateProductResponse pgr = {
      93           32 :     .hr.http_status = (unsigned int) response_code,
      94              :     .hr.reply = json
      95              :   };
      96              : 
      97           32 :   gpp->job = NULL;
      98           32 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
      99              :               "Got /private/products/$PRODUCT_ID response with status code %u\n",
     100              :               (unsigned int) response_code);
     101           32 :   switch (response_code)
     102              :   {
     103           18 :   case MHD_HTTP_OK:
     104              :     {
     105           18 :       const json_t *jcategories = NULL;
     106              :       struct GNUNET_JSON_Specification spec[] = {
     107           18 :         GNUNET_JSON_spec_string (
     108              :           "product_name",
     109              :           &pgr.details.ok.product_name),
     110           18 :         GNUNET_JSON_spec_string (
     111              :           "description",
     112              :           &pgr.details.ok.description),
     113           18 :         GNUNET_JSON_spec_object_const (
     114              :           "description_i18n",
     115              :           &pgr.details.ok.description_i18n),
     116           18 :         TALER_JSON_spec_slug (
     117              :           "unit",
     118              :           &pgr.details.ok.unit),
     119           18 :         TALER_JSON_spec_amount_any_array (
     120              :           "unit_price",
     121              :           &pgr.details.ok.unit_price_len,
     122              :           (struct TALER_Amount **) &pgr.details.ok.unit_price),
     123           18 :         TALER_JSON_spec_amount_any (
     124              :           "price",
     125              :           &pgr.details.ok.price),
     126           18 :         GNUNET_JSON_spec_mark_optional (
     127              :           GNUNET_JSON_spec_string (
     128              :             "image",
     129              :             &pgr.details.ok.image),
     130              :           NULL),
     131           18 :         GNUNET_JSON_spec_mark_optional (
     132              :           GNUNET_JSON_spec_array_const (
     133              :             "taxes",
     134              :             &pgr.details.ok.taxes),
     135              :           NULL),
     136           18 :         GNUNET_JSON_spec_int64 (
     137              :           "total_stock",
     138              :           &pgr.details.ok.total_stock),
     139           18 :         GNUNET_JSON_spec_string (
     140              :           "unit_total_stock",
     141              :           &pgr.details.ok.unit_total_stock),
     142           18 :         GNUNET_JSON_spec_bool (
     143              :           "unit_allow_fraction",
     144              :           &pgr.details.ok.unit_allow_fraction),
     145           18 :         GNUNET_JSON_spec_uint32 (
     146              :           "unit_precision_level",
     147              :           &pgr.details.ok.unit_precision_level),
     148           18 :         GNUNET_JSON_spec_uint64 (
     149              :           "total_sold",
     150              :           &pgr.details.ok.total_sold),
     151           18 :         GNUNET_JSON_spec_uint64 (
     152              :           "total_lost",
     153              :           &pgr.details.ok.total_lost),
     154           18 :         GNUNET_JSON_spec_mark_optional (
     155              :           GNUNET_JSON_spec_object_const (
     156              :             "address",
     157              :             &pgr.details.ok.location),
     158              :           NULL),
     159           18 :         GNUNET_JSON_spec_mark_optional (
     160              :           GNUNET_JSON_spec_timestamp (
     161              :             "next_restock",
     162              :             &pgr.details.ok.next_restock),
     163              :           NULL),
     164           18 :         GNUNET_JSON_spec_mark_optional (
     165              :           GNUNET_JSON_spec_array_const (
     166              :             "categories",
     167              :             &jcategories),
     168              :           NULL),
     169           18 :         GNUNET_JSON_spec_mark_optional (
     170              :           GNUNET_JSON_spec_uint64 (
     171              :             "product_group_id",
     172              :             &pgr.details.ok.product_group_id),
     173              :           NULL),
     174           18 :         GNUNET_JSON_spec_mark_optional (
     175              :           GNUNET_JSON_spec_uint64 (
     176              :             "money_pot_id",
     177              :             &pgr.details.ok.money_pot_id),
     178              :           NULL),
     179           18 :         GNUNET_JSON_spec_mark_optional (
     180              :           GNUNET_JSON_spec_bool (
     181              :             "price_is_net",
     182              :             &pgr.details.ok.price_is_net),
     183              :           NULL),
     184           18 :         GNUNET_JSON_spec_mark_optional (
     185              :           GNUNET_JSON_spec_uint16 (
     186              :             "minimum_age",
     187              :             &pgr.details.ok.minimum_age),
     188              :           NULL),
     189           18 :         GNUNET_JSON_spec_end ()
     190              :       };
     191              : 
     192           18 :       if (GNUNET_OK !=
     193           18 :           GNUNET_JSON_parse (json,
     194              :                              spec,
     195              :                              NULL, NULL))
     196              :       {
     197            0 :         pgr.hr.http_status = 0;
     198            0 :         pgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     199            0 :         break;
     200              :       }
     201              :       {
     202              :         unsigned int num_categories;
     203              :         uint64_t *categories;
     204              : 
     205           18 :         num_categories = (unsigned int) json_array_size (jcategories);
     206           18 :         if ( (json_array_size (jcategories) != (size_t) num_categories) ||
     207              :              (num_categories > MAX_CATEGORIES) )
     208              :         {
     209            0 :           GNUNET_break_op (0);
     210            0 :           pgr.hr.http_status = 0;
     211            0 :           pgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     212            0 :           break;
     213              :         }
     214           18 :         categories = GNUNET_new_array (num_categories,
     215              :                                        uint64_t);
     216           24 :         for (unsigned int i = 0; i < num_categories; i++)
     217              :         {
     218            6 :           json_t *jval = json_array_get (jcategories,
     219              :                                          i);
     220              : 
     221            6 :           if (! json_is_integer (jval))
     222              :           {
     223            0 :             GNUNET_break_op (0);
     224            0 :             GNUNET_free (categories);
     225            0 :             pgr.hr.http_status = 0;
     226            0 :             pgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     227            0 :             goto done;
     228              :           }
     229            6 :           categories[i] = (uint64_t) json_integer_value (jval);
     230              :         }
     231           18 :         pgr.details.ok.categories_len = num_categories;
     232           18 :         pgr.details.ok.categories = categories;
     233           18 :         gpp->cb (gpp->cb_cls,
     234              :                  &pgr);
     235           18 :         GNUNET_free (categories);
     236           18 :         TALER_MERCHANT_get_private_product_cancel (gpp);
     237           18 :         return;
     238              :       }
     239              :     }
     240            4 :   case MHD_HTTP_UNAUTHORIZED:
     241            4 :     pgr.hr.ec = TALER_JSON_get_error_code (json);
     242            4 :     pgr.hr.hint = TALER_JSON_get_error_hint (json);
     243            4 :     break;
     244           10 :   case MHD_HTTP_NOT_FOUND:
     245           10 :     pgr.hr.ec = TALER_JSON_get_error_code (json);
     246           10 :     pgr.hr.hint = TALER_JSON_get_error_hint (json);
     247           10 :     break;
     248            0 :   default:
     249            0 :     pgr.hr.ec = TALER_JSON_get_error_code (json);
     250            0 :     pgr.hr.hint = TALER_JSON_get_error_hint (json);
     251            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     252              :                 "Unexpected response code %u/%d\n",
     253              :                 (unsigned int) response_code,
     254              :                 (int) pgr.hr.ec);
     255            0 :     break;
     256              :   }
     257           14 : done:
     258           14 :   gpp->cb (gpp->cb_cls,
     259              :            &pgr);
     260           14 :   TALER_MERCHANT_get_private_product_cancel (gpp);
     261              : }
     262              : 
     263              : 
     264              : struct TALER_MERCHANT_GetPrivateProductHandle *
     265           32 : TALER_MERCHANT_get_private_product_create (
     266              :   struct GNUNET_CURL_Context *ctx,
     267              :   const char *url,
     268              :   const char *product_id)
     269              : {
     270              :   struct TALER_MERCHANT_GetPrivateProductHandle *gpp;
     271              : 
     272           32 :   gpp = GNUNET_new (struct TALER_MERCHANT_GetPrivateProductHandle);
     273           32 :   gpp->ctx = ctx;
     274           32 :   gpp->base_url = GNUNET_strdup (url);
     275           32 :   gpp->product_id = GNUNET_strdup (product_id);
     276           32 :   return gpp;
     277              : }
     278              : 
     279              : 
     280              : enum TALER_ErrorCode
     281           32 : TALER_MERCHANT_get_private_product_start (
     282              :   struct TALER_MERCHANT_GetPrivateProductHandle *gpp,
     283              :   TALER_MERCHANT_GetPrivateProductCallback cb,
     284              :   TALER_MERCHANT_GET_PRIVATE_PRODUCT_RESULT_CLOSURE *cb_cls)
     285              : {
     286              :   CURL *eh;
     287              : 
     288           32 :   gpp->cb = cb;
     289           32 :   gpp->cb_cls = cb_cls;
     290              :   {
     291              :     char *path;
     292              : 
     293           32 :     GNUNET_asprintf (&path,
     294              :                      "private/products/%s",
     295              :                      gpp->product_id);
     296           32 :     gpp->url = TALER_url_join (gpp->base_url,
     297              :                                path,
     298              :                                NULL);
     299           32 :     GNUNET_free (path);
     300              :   }
     301           32 :   if (NULL == gpp->url)
     302            0 :     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
     303           32 :   eh = TALER_MERCHANT_curl_easy_get_ (gpp->url);
     304           32 :   if (NULL == eh)
     305            0 :     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
     306           32 :   gpp->job = GNUNET_CURL_job_add (gpp->ctx,
     307              :                                   eh,
     308              :                                   &handle_get_product_finished,
     309              :                                   gpp);
     310           32 :   if (NULL == gpp->job)
     311            0 :     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
     312           32 :   return TALER_EC_NONE;
     313              : }
     314              : 
     315              : 
     316              : void
     317           32 : TALER_MERCHANT_get_private_product_cancel (
     318              :   struct TALER_MERCHANT_GetPrivateProductHandle *gpp)
     319              : {
     320           32 :   if (NULL != gpp->job)
     321              :   {
     322            0 :     GNUNET_CURL_job_cancel (gpp->job);
     323            0 :     gpp->job = NULL;
     324              :   }
     325           32 :   GNUNET_free (gpp->url);
     326           32 :   GNUNET_free (gpp->product_id);
     327           32 :   GNUNET_free (gpp->base_url);
     328           32 :   GNUNET_free (gpp);
     329           32 : }
     330              : 
     331              : 
     332              : /* end of merchant_api_get-private-products-PRODUCT_ID-new.c */
        

Generated by: LCOV version 2.0-1