Line data Source code
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/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 0 : parse_unit (const json_t *json,
84 : struct TALER_MERCHANT_GetPrivateUnitResponse *ugr)
85 : {
86 0 : struct TALER_MERCHANT_UnitEntry *entry = &ugr->details.ok.unit;
87 : struct GNUNET_JSON_Specification spec[] = {
88 0 : GNUNET_JSON_spec_string ("unit",
89 : &entry->unit),
90 0 : GNUNET_JSON_spec_string ("unit_name_long",
91 : &entry->unit_name_long),
92 0 : GNUNET_JSON_spec_string ("unit_name_short",
93 : &entry->unit_name_short),
94 0 : GNUNET_JSON_spec_mark_optional (
95 : GNUNET_JSON_spec_object_const ("unit_name_long_i18n",
96 : &entry->unit_name_long_i18n),
97 : NULL),
98 0 : GNUNET_JSON_spec_mark_optional (
99 : GNUNET_JSON_spec_object_const ("unit_name_short_i18n",
100 : &entry->unit_name_short_i18n),
101 : NULL),
102 0 : GNUNET_JSON_spec_bool ("unit_allow_fraction",
103 : &entry->unit_allow_fraction),
104 0 : GNUNET_JSON_spec_uint32 ("unit_precision_level",
105 : &entry->unit_precision_level),
106 0 : GNUNET_JSON_spec_bool ("unit_active",
107 : &entry->unit_active),
108 0 : GNUNET_JSON_spec_bool ("unit_builtin",
109 : &entry->unit_builtin),
110 0 : GNUNET_JSON_spec_uint64 ("unit_serial",
111 : &entry->unit_serial),
112 0 : GNUNET_JSON_spec_end ()
113 : };
114 :
115 0 : memset (entry,
116 : 0,
117 : sizeof (*entry));
118 0 : if (GNUNET_OK !=
119 0 : GNUNET_JSON_parse (json,
120 : spec,
121 : NULL,
122 : NULL))
123 : {
124 0 : GNUNET_break_op (0);
125 0 : return GNUNET_SYSERR;
126 : }
127 0 : 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 0 : handle_get_unit_finished (void *cls,
141 : long response_code,
142 : const void *response)
143 : {
144 0 : struct TALER_MERCHANT_GetPrivateUnitHandle *gpu = cls;
145 0 : const json_t *json = response;
146 0 : struct TALER_MERCHANT_GetPrivateUnitResponse ugr = {
147 0 : .hr.http_status = (unsigned int) response_code,
148 : .hr.reply = json
149 : };
150 :
151 0 : gpu->job = NULL;
152 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
153 : "Got /private/units/$UNIT response with status code %u\n",
154 : (unsigned int) response_code);
155 0 : switch (response_code)
156 : {
157 0 : case MHD_HTTP_OK:
158 0 : if (GNUNET_OK !=
159 0 : parse_unit (json,
160 : &ugr))
161 : {
162 0 : ugr.hr.http_status = 0;
163 0 : ugr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
164 0 : break;
165 : }
166 0 : gpu->cb (gpu->cb_cls,
167 : &ugr);
168 0 : TALER_MERCHANT_get_private_unit_cancel (gpu);
169 0 : return;
170 0 : case MHD_HTTP_UNAUTHORIZED:
171 : case MHD_HTTP_FORBIDDEN:
172 : case MHD_HTTP_NOT_FOUND:
173 0 : ugr.hr.ec = TALER_JSON_get_error_code (json);
174 0 : ugr.hr.hint = TALER_JSON_get_error_hint (json);
175 0 : break;
176 0 : default:
177 0 : ugr.hr.ec = TALER_JSON_get_error_code (json);
178 0 : ugr.hr.hint = TALER_JSON_get_error_hint (json);
179 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
180 : "Unexpected response code %u/%d\n",
181 : (unsigned int) response_code,
182 : (int) ugr.hr.ec);
183 0 : break;
184 : }
185 0 : gpu->cb (gpu->cb_cls,
186 : &ugr);
187 0 : TALER_MERCHANT_get_private_unit_cancel (gpu);
188 : }
189 :
190 :
191 : struct TALER_MERCHANT_GetPrivateUnitHandle *
192 0 : 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 0 : gpu = GNUNET_new (struct TALER_MERCHANT_GetPrivateUnitHandle);
200 0 : gpu->ctx = ctx;
201 0 : gpu->base_url = GNUNET_strdup (url);
202 0 : gpu->unit_id = GNUNET_strdup (unit_id);
203 0 : return gpu;
204 : }
205 :
206 :
207 : enum TALER_ErrorCode
208 0 : 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 0 : gpu->cb = cb;
216 0 : gpu->cb_cls = cb_cls;
217 : {
218 : char *path;
219 :
220 0 : GNUNET_asprintf (&path,
221 : "private/units/%s",
222 : gpu->unit_id);
223 0 : gpu->url = TALER_url_join (gpu->base_url,
224 : path,
225 : NULL);
226 0 : GNUNET_free (path);
227 : }
228 0 : if (NULL == gpu->url)
229 0 : return TALER_EC_GENERIC_CONFIGURATION_INVALID;
230 0 : eh = TALER_MERCHANT_curl_easy_get_ (gpu->url);
231 0 : if (NULL == eh)
232 0 : return TALER_EC_GENERIC_CONFIGURATION_INVALID;
233 0 : gpu->job = GNUNET_CURL_job_add (gpu->ctx,
234 : eh,
235 : &handle_get_unit_finished,
236 : gpu);
237 0 : if (NULL == gpu->job)
238 0 : return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
239 0 : return TALER_EC_NONE;
240 : }
241 :
242 :
243 : void
244 0 : TALER_MERCHANT_get_private_unit_cancel (
245 : struct TALER_MERCHANT_GetPrivateUnitHandle *gpu)
246 : {
247 0 : if (NULL != gpu->job)
248 : {
249 0 : GNUNET_CURL_job_cancel (gpu->job);
250 0 : gpu->job = NULL;
251 : }
252 0 : GNUNET_free (gpu->url);
253 0 : GNUNET_free (gpu->base_url);
254 0 : GNUNET_free (gpu->unit_id);
255 0 : GNUNET_free (gpu);
256 0 : }
257 :
258 :
259 : /* end of merchant_api_get-private-units-UNIT.c */
|