merchant_api_get_units.c (8790B)
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 7 Software 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 12 details. 13 14 You should have received a copy of the GNU Lesser General Public License along 15 with TALER; see the file COPYING.LGPL. If not, see 16 <http://www.gnu.org/licenses/> 17 */ 18 /** 19 * @file merchant_api_get_units.c 20 * @brief Implementation of GET /private/units 21 * @author Bohdan Potuzhnyi 22 */ 23 #include "platform.h" 24 #include <curl/curl.h> 25 #include <jansson.h> 26 #include <microhttpd.h> 27 #include <gnunet/gnunet_util_lib.h> 28 #include <gnunet/gnunet_curl_lib.h> 29 #include "taler_merchant_service.h" 30 #include "merchant_api_curl_defaults.h" 31 #include <taler/taler_json_lib.h> 32 33 34 /** 35 * Maximum number of units returned in a single response. 36 */ 37 #define MAX_UNITS 1024 38 39 40 /** 41 * Handle for a GET /private/units operation. 42 */ 43 struct TALER_MERCHANT_UnitsGetHandle 44 { 45 /** 46 * Fully qualified request URL. 47 */ 48 char *url; 49 50 /** 51 * In-flight job handle. 52 */ 53 struct GNUNET_CURL_Job *job; 54 55 /** 56 * Callback to invoke with the outcome. 57 */ 58 TALER_MERCHANT_UnitsGetCallback cb; 59 60 /** 61 * Closure for @e cb. 62 */ 63 void *cb_cls; 64 65 /** 66 * Execution context. 67 */ 68 struct GNUNET_CURL_Context *ctx; 69 }; 70 71 72 /** 73 * Parse an individual unit entry from @a value. 74 * 75 * @param value JSON object describing the unit 76 * @param[out] ue set to the parsed values 77 * @return #GNUNET_OK on success 78 */ 79 static enum GNUNET_GenericReturnValue 80 parse_unit_entry (const json_t *value, 81 struct TALER_MERCHANT_UnitEntry *ue) 82 { 83 const char *unit; 84 const char *unit_name_long; 85 const char *unit_name_short; 86 const json_t *unit_name_long_i18n = NULL; 87 const json_t *unit_name_short_i18n = NULL; 88 bool unit_allow_fraction; 89 bool unit_active; 90 bool unit_builtin; 91 uint32_t unit_precision_level; 92 struct GNUNET_JSON_Specification spec[] = { 93 GNUNET_JSON_spec_string ("unit", 94 &unit), 95 GNUNET_JSON_spec_string ("unit_name_long", 96 &unit_name_long), 97 GNUNET_JSON_spec_string ("unit_name_short", 98 &unit_name_short), 99 GNUNET_JSON_spec_mark_optional ( 100 GNUNET_JSON_spec_object_const ("unit_name_long_i18n", 101 &unit_name_long_i18n), 102 NULL), 103 GNUNET_JSON_spec_mark_optional ( 104 GNUNET_JSON_spec_object_const ("unit_name_short_i18n", 105 &unit_name_short_i18n), 106 NULL), 107 GNUNET_JSON_spec_bool ("unit_allow_fraction", 108 &unit_allow_fraction), 109 GNUNET_JSON_spec_uint32 ("unit_precision_level", 110 &unit_precision_level), 111 GNUNET_JSON_spec_bool ("unit_active", 112 &unit_active), 113 GNUNET_JSON_spec_bool ("unit_builtin", 114 &unit_builtin), 115 GNUNET_JSON_spec_end () 116 }; 117 118 if (GNUNET_OK != 119 GNUNET_JSON_parse (value, 120 spec, 121 NULL, 122 NULL)) 123 { 124 GNUNET_break_op (0); 125 GNUNET_JSON_parse_free (spec); 126 return GNUNET_SYSERR; 127 } 128 GNUNET_JSON_parse_free (spec); 129 ue->unit = unit; 130 ue->unit_name_long = unit_name_long; 131 ue->unit_name_short = unit_name_short; 132 ue->unit_name_long_i18n = unit_name_long_i18n; 133 ue->unit_name_short_i18n = unit_name_short_i18n; 134 ue->unit_allow_fraction = unit_allow_fraction; 135 ue->unit_precision_level = unit_precision_level; 136 ue->unit_active = unit_active; 137 ue->unit_builtin = unit_builtin; 138 return GNUNET_OK; 139 } 140 141 142 /** 143 * Parse the list of units from @a units and call the callback. 144 * 145 * @param json complete response JSON 146 * @param units array of units 147 * @param ugh ongoing operation handle 148 * @return #GNUNET_OK on success 149 */ 150 static enum GNUNET_GenericReturnValue 151 parse_units (const json_t *json, 152 const json_t *units, 153 struct TALER_MERCHANT_UnitsGetHandle *ugh) 154 { 155 size_t len; 156 157 len = json_array_size (units); 158 if (len > MAX_UNITS) 159 { 160 GNUNET_break_op (0); 161 return GNUNET_SYSERR; 162 } 163 164 { 165 struct TALER_MERCHANT_UnitEntry entries[GNUNET_NZL (len)]; 166 size_t idx; 167 json_t *value; 168 169 json_array_foreach (units, idx, value) { 170 if (GNUNET_OK != 171 parse_unit_entry (value, 172 &entries[idx])) 173 { 174 GNUNET_break_op (0); 175 return GNUNET_SYSERR; 176 } 177 } 178 { 179 struct TALER_MERCHANT_UnitsGetResponse ugr = { 180 .hr.http_status = MHD_HTTP_OK, 181 .hr.reply = json, 182 .details = { 183 .ok = { 184 .units = entries, 185 .units_length = (unsigned int) len 186 } 187 188 189 } 190 191 192 }; 193 194 ugh->cb (ugh->cb_cls, 195 &ugr); 196 } 197 } 198 return GNUNET_OK; 199 } 200 201 202 /** 203 * Called when the HTTP transfer finishes. 204 * 205 * @param cls closure, the operation handle 206 * @param response_code HTTP status (0 on network errors) 207 * @param response parsed JSON body (NULL if parsing failed) 208 */ 209 static void 210 handle_get_units_finished (void *cls, 211 long response_code, 212 const void *response) 213 { 214 struct TALER_MERCHANT_UnitsGetHandle *ugh = cls; 215 const json_t *json = response; 216 struct TALER_MERCHANT_UnitsGetResponse ugr = { 217 .hr.http_status = (unsigned int) response_code, 218 .hr.reply = json 219 }; 220 221 ugh->job = NULL; 222 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 223 "GET /private/units finished with status %u\n", 224 (unsigned int) response_code); 225 switch (response_code) 226 { 227 case MHD_HTTP_OK: 228 { 229 const json_t *units; 230 struct GNUNET_JSON_Specification spec[] = { 231 GNUNET_JSON_spec_array_const ("units", 232 &units), 233 GNUNET_JSON_spec_end () 234 }; 235 236 if (GNUNET_OK != 237 GNUNET_JSON_parse (json, 238 spec, 239 NULL, 240 NULL)) 241 { 242 GNUNET_break_op (0); 243 ugr.hr.http_status = 0; 244 ugr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 245 break; 246 } 247 if (GNUNET_OK == 248 parse_units (json, 249 units, 250 ugh)) 251 { 252 TALER_MERCHANT_units_get_cancel (ugh); 253 return; 254 } 255 ugr.hr.http_status = 0; 256 ugr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 257 break; 258 } 259 case MHD_HTTP_UNAUTHORIZED: 260 case MHD_HTTP_FORBIDDEN: 261 case MHD_HTTP_NOT_FOUND: 262 case MHD_HTTP_CONFLICT: 263 ugr.hr.ec = TALER_JSON_get_error_code (json); 264 ugr.hr.hint = TALER_JSON_get_error_hint (json); 265 break; 266 case 0: 267 ugr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 268 break; 269 default: 270 ugr.hr.ec = TALER_JSON_get_error_code (json); 271 ugr.hr.hint = TALER_JSON_get_error_hint (json); 272 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 273 "Unexpected response code %u/%d for GET /private/units\n", 274 (unsigned int) response_code, 275 (int) ugr.hr.ec); 276 break; 277 } 278 ugh->cb (ugh->cb_cls, 279 &ugr); 280 TALER_MERCHANT_units_get_cancel (ugh); 281 } 282 283 284 struct TALER_MERCHANT_UnitsGetHandle * 285 TALER_MERCHANT_units_get (struct GNUNET_CURL_Context *ctx, 286 const char *backend_url, 287 TALER_MERCHANT_UnitsGetCallback cb, 288 void *cb_cls) 289 { 290 struct TALER_MERCHANT_UnitsGetHandle *ugh; 291 CURL *eh; 292 293 ugh = GNUNET_new (struct TALER_MERCHANT_UnitsGetHandle); 294 ugh->ctx = ctx; 295 ugh->cb = cb; 296 ugh->cb_cls = cb_cls; 297 ugh->url = TALER_url_join (backend_url, 298 "private/units", 299 NULL); 300 if (NULL == ugh->url) 301 { 302 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 303 "Failed to build /private/units URL\n"); 304 GNUNET_free (ugh); 305 return NULL; 306 } 307 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 308 "Requesting URL '%s'\n", 309 ugh->url); 310 eh = TALER_MERCHANT_curl_easy_get_ (ugh->url); 311 ugh->job = GNUNET_CURL_job_add (ctx, 312 eh, 313 &handle_get_units_finished, 314 ugh); 315 return ugh; 316 } 317 318 319 void 320 TALER_MERCHANT_units_get_cancel (struct TALER_MERCHANT_UnitsGetHandle *ugh) 321 { 322 if (NULL != ugh->job) 323 GNUNET_CURL_job_cancel (ugh->job); 324 GNUNET_free (ugh->url); 325 GNUNET_free (ugh); 326 } 327 328 329 /* end of merchant_api_get_units.c */