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