merchant_api_get-private-units-UNIT.c (7259B)
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 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-units-UNIT.c 19 * @brief Implementation of the GET /private/units/$UNIT 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-units-UNIT.h> 29 #include "merchant_api_curl_defaults.h" 30 #include <taler/taler_json_lib.h> 31 32 33 /** 34 * Handle for a GET /private/units/$UNIT operation. 35 */ 36 struct TALER_MERCHANT_GetPrivateUnitHandle 37 { 38 /** 39 * Base URL of the merchant backend. 40 */ 41 char *base_url; 42 43 /** 44 * The full URL for this request. 45 */ 46 char *url; 47 48 /** 49 * Handle for the request. 50 */ 51 struct GNUNET_CURL_Job *job; 52 53 /** 54 * Function to call with the result. 55 */ 56 TALER_MERCHANT_GetPrivateUnitCallback cb; 57 58 /** 59 * Closure for @a cb. 60 */ 61 TALER_MERCHANT_GET_PRIVATE_UNIT_RESULT_CLOSURE *cb_cls; 62 63 /** 64 * Reference to the execution context. 65 */ 66 struct GNUNET_CURL_Context *ctx; 67 68 /** 69 * Unit identifier. 70 */ 71 char *unit_id; 72 }; 73 74 75 /** 76 * Parse the JSON response into the unit entry. 77 * 78 * @param json full JSON reply 79 * @param ugr response descriptor to populate 80 * @return #GNUNET_OK on success 81 */ 82 static enum GNUNET_GenericReturnValue 83 parse_unit (const json_t *json, 84 struct TALER_MERCHANT_GetPrivateUnitResponse *ugr) 85 { 86 struct TALER_MERCHANT_UnitEntry *entry = &ugr->details.ok.unit; 87 struct GNUNET_JSON_Specification spec[] = { 88 GNUNET_JSON_spec_string ("unit", 89 &entry->unit), 90 GNUNET_JSON_spec_string ("unit_name_long", 91 &entry->unit_name_long), 92 GNUNET_JSON_spec_string ("unit_name_short", 93 &entry->unit_name_short), 94 GNUNET_JSON_spec_mark_optional ( 95 GNUNET_JSON_spec_object_const ("unit_name_long_i18n", 96 &entry->unit_name_long_i18n), 97 NULL), 98 GNUNET_JSON_spec_mark_optional ( 99 GNUNET_JSON_spec_object_const ("unit_name_short_i18n", 100 &entry->unit_name_short_i18n), 101 NULL), 102 GNUNET_JSON_spec_bool ("unit_allow_fraction", 103 &entry->unit_allow_fraction), 104 GNUNET_JSON_spec_uint32 ("unit_precision_level", 105 &entry->unit_precision_level), 106 GNUNET_JSON_spec_bool ("unit_active", 107 &entry->unit_active), 108 GNUNET_JSON_spec_bool ("unit_builtin", 109 &entry->unit_builtin), 110 GNUNET_JSON_spec_uint64 ("unit_serial", 111 &entry->unit_serial), 112 GNUNET_JSON_spec_end () 113 }; 114 115 memset (entry, 116 0, 117 sizeof (*entry)); 118 if (GNUNET_OK != 119 GNUNET_JSON_parse (json, 120 spec, 121 NULL, 122 NULL)) 123 { 124 GNUNET_break_op (0); 125 return GNUNET_SYSERR; 126 } 127 return GNUNET_OK; 128 } 129 130 131 /** 132 * Function called when we're done processing the 133 * HTTP GET /private/units/$UNIT request. 134 * 135 * @param cls the `struct TALER_MERCHANT_GetPrivateUnitHandle` 136 * @param response_code HTTP response code, 0 on error 137 * @param response response body, NULL if not in JSON 138 */ 139 static void 140 handle_get_unit_finished (void *cls, 141 long response_code, 142 const void *response) 143 { 144 struct TALER_MERCHANT_GetPrivateUnitHandle *gpu = cls; 145 const json_t *json = response; 146 struct TALER_MERCHANT_GetPrivateUnitResponse ugr = { 147 .hr.http_status = (unsigned int) response_code, 148 .hr.reply = json 149 }; 150 151 gpu->job = NULL; 152 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 153 "Got /private/units/$UNIT response with status code %u\n", 154 (unsigned int) response_code); 155 switch (response_code) 156 { 157 case MHD_HTTP_OK: 158 if (GNUNET_OK != 159 parse_unit (json, 160 &ugr)) 161 { 162 ugr.hr.http_status = 0; 163 ugr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 164 break; 165 } 166 gpu->cb (gpu->cb_cls, 167 &ugr); 168 TALER_MERCHANT_get_private_unit_cancel (gpu); 169 return; 170 case MHD_HTTP_UNAUTHORIZED: 171 case MHD_HTTP_FORBIDDEN: 172 case MHD_HTTP_NOT_FOUND: 173 ugr.hr.ec = TALER_JSON_get_error_code (json); 174 ugr.hr.hint = TALER_JSON_get_error_hint (json); 175 break; 176 default: 177 ugr.hr.ec = TALER_JSON_get_error_code (json); 178 ugr.hr.hint = TALER_JSON_get_error_hint (json); 179 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 180 "Unexpected response code %u/%d\n", 181 (unsigned int) response_code, 182 (int) ugr.hr.ec); 183 break; 184 } 185 gpu->cb (gpu->cb_cls, 186 &ugr); 187 TALER_MERCHANT_get_private_unit_cancel (gpu); 188 } 189 190 191 struct TALER_MERCHANT_GetPrivateUnitHandle * 192 TALER_MERCHANT_get_private_unit_create ( 193 struct GNUNET_CURL_Context *ctx, 194 const char *url, 195 const char *unit_id) 196 { 197 struct TALER_MERCHANT_GetPrivateUnitHandle *gpu; 198 199 gpu = GNUNET_new (struct TALER_MERCHANT_GetPrivateUnitHandle); 200 gpu->ctx = ctx; 201 gpu->base_url = GNUNET_strdup (url); 202 gpu->unit_id = GNUNET_strdup (unit_id); 203 return gpu; 204 } 205 206 207 enum TALER_ErrorCode 208 TALER_MERCHANT_get_private_unit_start ( 209 struct TALER_MERCHANT_GetPrivateUnitHandle *gpu, 210 TALER_MERCHANT_GetPrivateUnitCallback cb, 211 TALER_MERCHANT_GET_PRIVATE_UNIT_RESULT_CLOSURE *cb_cls) 212 { 213 CURL *eh; 214 215 gpu->cb = cb; 216 gpu->cb_cls = cb_cls; 217 { 218 char *path; 219 220 GNUNET_asprintf (&path, 221 "private/units/%s", 222 gpu->unit_id); 223 gpu->url = TALER_url_join (gpu->base_url, 224 path, 225 NULL); 226 GNUNET_free (path); 227 } 228 if (NULL == gpu->url) 229 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 230 eh = TALER_MERCHANT_curl_easy_get_ (gpu->url); 231 if (NULL == eh) 232 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 233 gpu->job = GNUNET_CURL_job_add (gpu->ctx, 234 eh, 235 &handle_get_unit_finished, 236 gpu); 237 if (NULL == gpu->job) 238 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 239 return TALER_EC_NONE; 240 } 241 242 243 void 244 TALER_MERCHANT_get_private_unit_cancel ( 245 struct TALER_MERCHANT_GetPrivateUnitHandle *gpu) 246 { 247 if (NULL != gpu->job) 248 { 249 GNUNET_CURL_job_cancel (gpu->job); 250 gpu->job = NULL; 251 } 252 GNUNET_free (gpu->url); 253 GNUNET_free (gpu->base_url); 254 GNUNET_free (gpu->unit_id); 255 GNUNET_free (gpu); 256 } 257 258 259 /* end of merchant_api_get-private-units-UNIT.c */