merchant_api_post-private-units.c (9803B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2025-2026 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify 6 it under the terms of the GNU Lesser General Public License as 7 published by the Free Software Foundation; either version 2.1, 8 or (at your option) any later version. 9 10 TALER is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU Lesser General Public License for more details. 14 15 You should have received a copy of the GNU Lesser General 16 Public License along with TALER; see the file COPYING.LGPL. 17 If not, see <http://www.gnu.org/licenses/> 18 */ 19 /** 20 * @file merchant_api_post-private-units-new.c 21 * @brief Implementation of the POST /private/units request 22 * @author Christian Grothoff 23 */ 24 #include "taler/platform.h" 25 #include <curl/curl.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/taler-merchant/post-private-units.h> 31 #include "merchant_api_curl_defaults.h" 32 #include "merchant_api_common.h" 33 #include <taler/taler_json_lib.h> 34 #include <taler/taler_curl_lib.h> 35 36 37 /** 38 * Handle for a POST /private/units operation. 39 */ 40 struct TALER_MERCHANT_PostPrivateUnitsHandle 41 { 42 /** 43 * Base URL of the merchant backend. 44 */ 45 char *base_url; 46 47 /** 48 * The full URL for this request. 49 */ 50 char *url; 51 52 /** 53 * Handle for the request. 54 */ 55 struct GNUNET_CURL_Job *job; 56 57 /** 58 * Function to call with the result. 59 */ 60 TALER_MERCHANT_PostPrivateUnitsCallback cb; 61 62 /** 63 * Closure for @a cb. 64 */ 65 TALER_MERCHANT_POST_PRIVATE_UNITS_RESULT_CLOSURE *cb_cls; 66 67 /** 68 * Reference to the execution context. 69 */ 70 struct GNUNET_CURL_Context *ctx; 71 72 /** 73 * Minor context that holds body and headers. 74 */ 75 struct TALER_CURL_PostContext post_ctx; 76 77 /** 78 * Unit identifier. 79 */ 80 char *unit_id; 81 82 /** 83 * Long human-readable name. 84 */ 85 char *unit_name_long; 86 87 /** 88 * Short symbol for the unit. 89 */ 90 char *unit_name_short; 91 92 /** 93 * Whether fractional quantities are allowed. 94 */ 95 bool unit_allow_fraction; 96 97 /** 98 * Precision level for fractional quantities. 99 */ 100 uint32_t unit_precision_level; 101 102 /** 103 * Whether the unit is active. 104 */ 105 bool unit_active; 106 107 /** 108 * Whether @e unit_allow_fraction was explicitly set via options. 109 */ 110 bool have_unit_allow_fraction; 111 112 /** 113 * Whether @e unit_precision_level was explicitly set via options. 114 */ 115 bool have_unit_precision_level; 116 117 /** 118 * Whether @e unit_active was explicitly set via options. 119 */ 120 bool have_unit_active; 121 122 /** 123 * Optional internationalized long names (JSON). 124 */ 125 json_t *unit_name_long_i18n; 126 127 /** 128 * Optional internationalized short names (JSON). 129 */ 130 json_t *unit_name_short_i18n; 131 }; 132 133 134 /** 135 * Function called when we're done processing the 136 * HTTP POST /private/units request. 137 * 138 * @param cls the `struct TALER_MERCHANT_PostPrivateUnitsHandle` 139 * @param response_code HTTP response code, 0 on error 140 * @param response response body, NULL if not in JSON 141 */ 142 static void 143 handle_post_units_finished (void *cls, 144 long response_code, 145 const void *response) 146 { 147 struct TALER_MERCHANT_PostPrivateUnitsHandle *ppuh = cls; 148 const json_t *json = response; 149 struct TALER_MERCHANT_PostPrivateUnitsResponse pur = { 150 .hr.http_status = (unsigned int) response_code, 151 .hr.reply = json 152 }; 153 154 ppuh->job = NULL; 155 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 156 "POST /private/units completed with response code %u\n", 157 (unsigned int) response_code); 158 switch (response_code) 159 { 160 case 0: 161 pur.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 162 break; 163 case MHD_HTTP_NO_CONTENT: 164 break; 165 case MHD_HTTP_BAD_REQUEST: 166 case MHD_HTTP_UNAUTHORIZED: 167 case MHD_HTTP_FORBIDDEN: 168 case MHD_HTTP_NOT_FOUND: 169 case MHD_HTTP_CONFLICT: 170 case MHD_HTTP_INTERNAL_SERVER_ERROR: 171 pur.hr.ec = TALER_JSON_get_error_code (json); 172 pur.hr.hint = TALER_JSON_get_error_hint (json); 173 break; 174 default: 175 TALER_MERCHANT_parse_error_details_ (json, 176 response_code, 177 &pur.hr); 178 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 179 "Unexpected response code %u/%d\n", 180 (unsigned int) response_code, 181 (int) pur.hr.ec); 182 GNUNET_break_op (0); 183 break; 184 } 185 ppuh->cb (ppuh->cb_cls, 186 &pur); 187 TALER_MERCHANT_post_private_units_cancel (ppuh); 188 } 189 190 191 struct TALER_MERCHANT_PostPrivateUnitsHandle * 192 TALER_MERCHANT_post_private_units_create ( 193 struct GNUNET_CURL_Context *ctx, 194 const char *url, 195 const char *unit_id, 196 const char *unit_name_long, 197 const char *unit_name_short) 198 { 199 struct TALER_MERCHANT_PostPrivateUnitsHandle *ppuh; 200 201 ppuh = GNUNET_new (struct TALER_MERCHANT_PostPrivateUnitsHandle); 202 ppuh->ctx = ctx; 203 ppuh->base_url = GNUNET_strdup (url); 204 ppuh->unit_id = GNUNET_strdup (unit_id); 205 ppuh->unit_name_long = GNUNET_strdup (unit_name_long); 206 ppuh->unit_name_short = GNUNET_strdup (unit_name_short); 207 return ppuh; 208 } 209 210 211 enum GNUNET_GenericReturnValue 212 TALER_MERCHANT_post_private_units_set_options_ ( 213 struct TALER_MERCHANT_PostPrivateUnitsHandle *ppuh, 214 unsigned int num_options, 215 const struct TALER_MERCHANT_PostPrivateUnitsOptionValue *options) 216 { 217 for (unsigned int i = 0; i < num_options; i++) 218 { 219 switch (options[i].option) 220 { 221 case TALER_MERCHANT_POST_PRIVATE_UNITS_OPTION_END: 222 return GNUNET_OK; 223 case TALER_MERCHANT_POST_PRIVATE_UNITS_OPTION_UNIT_NAME_LONG_I18N: 224 ppuh->unit_name_long_i18n 225 = json_incref ((json_t *) options[i].details.unit_name_long_i18n); 226 break; 227 case TALER_MERCHANT_POST_PRIVATE_UNITS_OPTION_UNIT_NAME_SHORT_I18N: 228 ppuh->unit_name_short_i18n 229 = json_incref ((json_t *) options[i].details.unit_name_short_i18n); 230 break; 231 case TALER_MERCHANT_POST_PRIVATE_UNITS_OPTION_UNIT_ALLOW_FRACTION: 232 ppuh->unit_allow_fraction = options[i].details.unit_allow_fraction; 233 ppuh->have_unit_allow_fraction = true; 234 break; 235 case TALER_MERCHANT_POST_PRIVATE_UNITS_OPTION_UNIT_PRECISION_LEVEL: 236 ppuh->unit_precision_level = options[i].details.unit_precision_level; 237 ppuh->have_unit_precision_level = true; 238 break; 239 case TALER_MERCHANT_POST_PRIVATE_UNITS_OPTION_UNIT_ACTIVE: 240 ppuh->unit_active = options[i].details.unit_active; 241 ppuh->have_unit_active = true; 242 break; 243 default: 244 GNUNET_break (0); 245 return GNUNET_SYSERR; 246 } 247 } 248 return GNUNET_OK; 249 } 250 251 252 enum TALER_ErrorCode 253 TALER_MERCHANT_post_private_units_start ( 254 struct TALER_MERCHANT_PostPrivateUnitsHandle *ppuh, 255 TALER_MERCHANT_PostPrivateUnitsCallback cb, 256 TALER_MERCHANT_POST_PRIVATE_UNITS_RESULT_CLOSURE *cb_cls) 257 { 258 json_t *req_obj; 259 CURL *eh; 260 261 ppuh->cb = cb; 262 ppuh->cb_cls = cb_cls; 263 ppuh->url = TALER_url_join (ppuh->base_url, 264 "private/units", 265 NULL); 266 if (NULL == ppuh->url) 267 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 268 req_obj = GNUNET_JSON_PACK ( 269 GNUNET_JSON_pack_string ("unit", 270 ppuh->unit_id), 271 GNUNET_JSON_pack_string ("unit_name_long", 272 ppuh->unit_name_long), 273 GNUNET_JSON_pack_string ("unit_name_short", 274 ppuh->unit_name_short), 275 GNUNET_JSON_pack_bool ("unit_allow_fraction", 276 ppuh->have_unit_allow_fraction 277 ? ppuh->unit_allow_fraction 278 : false), 279 GNUNET_JSON_pack_uint64 ("unit_precision_level", 280 ppuh->have_unit_precision_level 281 ? (uint64_t) ppuh->unit_precision_level 282 : 0), 283 GNUNET_JSON_pack_bool ("unit_active", 284 ppuh->have_unit_active 285 ? ppuh->unit_active 286 : true), 287 GNUNET_JSON_pack_allow_null ( 288 GNUNET_JSON_pack_object_incref ("unit_name_long_i18n", 289 ppuh->unit_name_long_i18n)), 290 GNUNET_JSON_pack_allow_null ( 291 GNUNET_JSON_pack_object_incref ("unit_name_short_i18n", 292 ppuh->unit_name_short_i18n))); 293 eh = TALER_MERCHANT_curl_easy_get_ (ppuh->url); 294 if ( (NULL == eh) || 295 (GNUNET_OK != 296 TALER_curl_easy_post (&ppuh->post_ctx, 297 eh, 298 req_obj)) ) 299 { 300 GNUNET_break (0); 301 json_decref (req_obj); 302 if (NULL != eh) 303 curl_easy_cleanup (eh); 304 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 305 } 306 json_decref (req_obj); 307 ppuh->job = GNUNET_CURL_job_add2 (ppuh->ctx, 308 eh, 309 ppuh->post_ctx.headers, 310 &handle_post_units_finished, 311 ppuh); 312 if (NULL == ppuh->job) 313 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 314 return TALER_EC_NONE; 315 } 316 317 318 void 319 TALER_MERCHANT_post_private_units_cancel ( 320 struct TALER_MERCHANT_PostPrivateUnitsHandle *ppuh) 321 { 322 if (NULL != ppuh->job) 323 { 324 GNUNET_CURL_job_cancel (ppuh->job); 325 ppuh->job = NULL; 326 } 327 TALER_curl_easy_post_finished (&ppuh->post_ctx); 328 json_decref (ppuh->unit_name_long_i18n); 329 json_decref (ppuh->unit_name_short_i18n); 330 GNUNET_free (ppuh->unit_id); 331 GNUNET_free (ppuh->unit_name_long); 332 GNUNET_free (ppuh->unit_name_short); 333 GNUNET_free (ppuh->url); 334 GNUNET_free (ppuh->base_url); 335 GNUNET_free (ppuh); 336 } 337 338 339 /* end of merchant_api_post-private-units-new.c */