merchant_api_get_unit.c (7167B)
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 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 <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file merchant_api_get_unit.c 18 * @brief Implementation of GET /private/units/$ID 19 * @author Bohdan Potuzhnyi 20 */ 21 #include "platform.h" 22 #include <curl/curl.h> 23 #include <jansson.h> 24 #include <microhttpd.h> 25 #include <gnunet/gnunet_util_lib.h> 26 #include <gnunet/gnunet_curl_lib.h> 27 #include "taler_merchant_service.h" 28 #include "merchant_api_curl_defaults.h" 29 #include <taler/taler_json_lib.h> 30 31 32 /** 33 * Handle for a GET /private/units/$ID operation. 34 */ 35 struct TALER_MERCHANT_UnitGetHandle 36 { 37 /** 38 * Fully qualified request URL. 39 */ 40 char *url; 41 42 /** 43 * In-flight job handle. 44 */ 45 struct GNUNET_CURL_Job *job; 46 47 /** 48 * Callback to invoke with the response. 49 */ 50 TALER_MERCHANT_UnitGetCallback cb; 51 52 /** 53 * Closure for @a cb. 54 */ 55 void *cb_cls; 56 57 /** 58 * Execution context. 59 */ 60 struct GNUNET_CURL_Context *ctx; 61 }; 62 63 64 /** 65 * Parse the JSON response into @a ugr. 66 * 67 * @param json full JSON reply 68 * @param ugr response descriptor to populate 69 * @return #GNUNET_OK on success 70 */ 71 static enum GNUNET_GenericReturnValue 72 parse_unit (const json_t *json, 73 struct TALER_MERCHANT_UnitGetResponse *ugr) 74 { 75 struct TALER_MERCHANT_UnitEntry *entry = &ugr->details.ok.unit; 76 const char *unit; 77 const char *unit_name_long; 78 const char *unit_name_short; 79 const json_t *unit_name_long_i18n = NULL; 80 const json_t *unit_name_short_i18n = NULL; 81 bool unit_allow_fraction; 82 bool unit_active; 83 bool unit_builtin; 84 uint32_t unit_precision_level; 85 struct GNUNET_JSON_Specification spec[] = { 86 GNUNET_JSON_spec_string ("unit", 87 &unit), 88 GNUNET_JSON_spec_string ("unit_name_long", 89 &unit_name_long), 90 GNUNET_JSON_spec_string ("unit_name_short", 91 &unit_name_short), 92 GNUNET_JSON_spec_mark_optional ( 93 GNUNET_JSON_spec_object_const ("unit_name_long_i18n", 94 &unit_name_long_i18n), 95 NULL), 96 GNUNET_JSON_spec_mark_optional ( 97 GNUNET_JSON_spec_object_const ("unit_name_short_i18n", 98 &unit_name_short_i18n), 99 NULL), 100 GNUNET_JSON_spec_bool ("unit_allow_fraction", 101 &unit_allow_fraction), 102 GNUNET_JSON_spec_uint32 ("unit_precision_level", 103 &unit_precision_level), 104 GNUNET_JSON_spec_bool ("unit_active", 105 &unit_active), 106 GNUNET_JSON_spec_bool ("unit_builtin", 107 &unit_builtin), 108 GNUNET_JSON_spec_end () 109 }; 110 111 if (GNUNET_OK != 112 GNUNET_JSON_parse (json, 113 spec, 114 NULL, 115 NULL)) 116 { 117 GNUNET_break_op (0); 118 GNUNET_JSON_parse_free (spec); 119 return GNUNET_SYSERR; 120 } 121 GNUNET_JSON_parse_free (spec); 122 entry->unit = unit; 123 entry->unit_name_long = unit_name_long; 124 entry->unit_name_short = unit_name_short; 125 entry->unit_name_long_i18n = unit_name_long_i18n; 126 entry->unit_name_short_i18n = unit_name_short_i18n; 127 entry->unit_allow_fraction = unit_allow_fraction; 128 entry->unit_precision_level = unit_precision_level; 129 entry->unit_active = unit_active; 130 entry->unit_builtin = unit_builtin; 131 return GNUNET_OK; 132 } 133 134 135 /** 136 * Called once the HTTP request completes. 137 * 138 * @param cls operation handle 139 * @param response_code HTTP status (0 on client-side errors) 140 * @param response parsed JSON reply (NULL if parsing failed) 141 */ 142 static void 143 handle_get_unit_finished (void *cls, 144 long response_code, 145 const void *response) 146 { 147 struct TALER_MERCHANT_UnitGetHandle *ugh = cls; 148 const json_t *json = response; 149 struct TALER_MERCHANT_UnitGetResponse ugr = { 150 .hr.http_status = (unsigned int) response_code, 151 .hr.reply = json 152 }; 153 154 ugh->job = NULL; 155 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 156 "GET /private/units/$ID finished with status %u\n", 157 (unsigned int) response_code); 158 switch (response_code) 159 { 160 case MHD_HTTP_OK: 161 if (GNUNET_OK != 162 parse_unit (json, 163 &ugr)) 164 { 165 ugr.hr.http_status = 0; 166 ugr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 167 break; 168 } 169 ugh->cb (ugh->cb_cls, 170 &ugr); 171 TALER_MERCHANT_unit_get_cancel (ugh); 172 return; 173 case MHD_HTTP_UNAUTHORIZED: 174 case MHD_HTTP_FORBIDDEN: 175 case MHD_HTTP_NOT_FOUND: 176 ugr.hr.ec = TALER_JSON_get_error_code (json); 177 ugr.hr.hint = TALER_JSON_get_error_hint (json); 178 break; 179 case 0: 180 ugr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 181 break; 182 default: 183 ugr.hr.ec = TALER_JSON_get_error_code (json); 184 ugr.hr.hint = TALER_JSON_get_error_hint (json); 185 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 186 "Unexpected response %u/%d for GET /private/units/$ID\n", 187 (unsigned int) response_code, 188 (int) ugr.hr.ec); 189 break; 190 } 191 ugh->cb (ugh->cb_cls, 192 &ugr); 193 TALER_MERCHANT_unit_get_cancel (ugh); 194 } 195 196 197 struct TALER_MERCHANT_UnitGetHandle * 198 TALER_MERCHANT_unit_get (struct GNUNET_CURL_Context *ctx, 199 const char *backend_url, 200 const char *unit_id, 201 TALER_MERCHANT_UnitGetCallback cb, 202 void *cb_cls) 203 { 204 struct TALER_MERCHANT_UnitGetHandle *ugh; 205 CURL *eh; 206 char *path; 207 208 GNUNET_asprintf (&path, 209 "private/units/%s", 210 unit_id); 211 ugh = GNUNET_new (struct TALER_MERCHANT_UnitGetHandle); 212 ugh->ctx = ctx; 213 ugh->cb = cb; 214 ugh->cb_cls = cb_cls; 215 ugh->url = TALER_url_join (backend_url, 216 path, 217 NULL); 218 GNUNET_free (path); 219 if (NULL == ugh->url) 220 { 221 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 222 "Failed to build /private/units/%s URL\n", 223 unit_id); 224 GNUNET_free (ugh); 225 return NULL; 226 } 227 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 228 "Requesting URL '%s'\n", 229 ugh->url); 230 eh = TALER_MERCHANT_curl_easy_get_ (ugh->url); 231 ugh->job = GNUNET_CURL_job_add (ctx, 232 eh, 233 &handle_get_unit_finished, 234 ugh); 235 return ugh; 236 } 237 238 239 void 240 TALER_MERCHANT_unit_get_cancel (struct TALER_MERCHANT_UnitGetHandle *ugh) 241 { 242 if (NULL != ugh->job) 243 GNUNET_CURL_job_cancel (ugh->job); 244 GNUNET_free (ugh->url); 245 GNUNET_free (ugh); 246 } 247 248 249 /* end of merchant_api_get_unit.c */