Line data Source code
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 4 : parse_unit (const json_t *json,
73 : struct TALER_MERCHANT_UnitGetResponse *ugr)
74 : {
75 4 : 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 4 : const json_t *unit_name_long_i18n = NULL;
80 4 : 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 4 : GNUNET_JSON_spec_string ("unit",
87 : &unit),
88 4 : GNUNET_JSON_spec_string ("unit_name_long",
89 : &unit_name_long),
90 4 : GNUNET_JSON_spec_string ("unit_name_short",
91 : &unit_name_short),
92 4 : GNUNET_JSON_spec_mark_optional (
93 : GNUNET_JSON_spec_object_const ("unit_name_long_i18n",
94 : &unit_name_long_i18n),
95 : NULL),
96 4 : GNUNET_JSON_spec_mark_optional (
97 : GNUNET_JSON_spec_object_const ("unit_name_short_i18n",
98 : &unit_name_short_i18n),
99 : NULL),
100 4 : GNUNET_JSON_spec_bool ("unit_allow_fraction",
101 : &unit_allow_fraction),
102 4 : GNUNET_JSON_spec_uint32 ("unit_precision_level",
103 : &unit_precision_level),
104 4 : GNUNET_JSON_spec_bool ("unit_active",
105 : &unit_active),
106 4 : GNUNET_JSON_spec_bool ("unit_builtin",
107 : &unit_builtin),
108 4 : GNUNET_JSON_spec_end ()
109 : };
110 :
111 4 : if (GNUNET_OK !=
112 4 : GNUNET_JSON_parse (json,
113 : spec,
114 : NULL,
115 : NULL))
116 : {
117 0 : GNUNET_break_op (0);
118 0 : GNUNET_JSON_parse_free (spec);
119 0 : return GNUNET_SYSERR;
120 : }
121 4 : GNUNET_JSON_parse_free (spec);
122 4 : entry->unit = unit;
123 4 : entry->unit_name_long = unit_name_long;
124 4 : entry->unit_name_short = unit_name_short;
125 4 : entry->unit_name_long_i18n = unit_name_long_i18n;
126 4 : entry->unit_name_short_i18n = unit_name_short_i18n;
127 4 : entry->unit_allow_fraction = unit_allow_fraction;
128 4 : entry->unit_precision_level = unit_precision_level;
129 4 : entry->unit_active = unit_active;
130 4 : entry->unit_builtin = unit_builtin;
131 4 : 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 6 : handle_get_unit_finished (void *cls,
144 : long response_code,
145 : const void *response)
146 : {
147 6 : struct TALER_MERCHANT_UnitGetHandle *ugh = cls;
148 6 : const json_t *json = response;
149 6 : struct TALER_MERCHANT_UnitGetResponse ugr = {
150 6 : .hr.http_status = (unsigned int) response_code,
151 : .hr.reply = json
152 : };
153 :
154 6 : ugh->job = NULL;
155 6 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
156 : "GET /private/units/$ID finished with status %u\n",
157 : (unsigned int) response_code);
158 6 : switch (response_code)
159 : {
160 4 : case MHD_HTTP_OK:
161 4 : if (GNUNET_OK !=
162 4 : parse_unit (json,
163 : &ugr))
164 : {
165 0 : ugr.hr.http_status = 0;
166 0 : ugr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
167 0 : break;
168 : }
169 4 : ugh->cb (ugh->cb_cls,
170 : &ugr);
171 4 : TALER_MERCHANT_unit_get_cancel (ugh);
172 4 : return;
173 2 : case MHD_HTTP_UNAUTHORIZED:
174 : case MHD_HTTP_FORBIDDEN:
175 : case MHD_HTTP_NOT_FOUND:
176 2 : ugr.hr.ec = TALER_JSON_get_error_code (json);
177 2 : ugr.hr.hint = TALER_JSON_get_error_hint (json);
178 2 : break;
179 0 : case 0:
180 0 : ugr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
181 0 : break;
182 0 : default:
183 0 : ugr.hr.ec = TALER_JSON_get_error_code (json);
184 0 : ugr.hr.hint = TALER_JSON_get_error_hint (json);
185 0 : 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 0 : break;
190 : }
191 2 : ugh->cb (ugh->cb_cls,
192 : &ugr);
193 2 : TALER_MERCHANT_unit_get_cancel (ugh);
194 : }
195 :
196 :
197 : struct TALER_MERCHANT_UnitGetHandle *
198 6 : 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 6 : GNUNET_asprintf (&path,
209 : "private/units/%s",
210 : unit_id);
211 6 : ugh = GNUNET_new (struct TALER_MERCHANT_UnitGetHandle);
212 6 : ugh->ctx = ctx;
213 6 : ugh->cb = cb;
214 6 : ugh->cb_cls = cb_cls;
215 6 : ugh->url = TALER_url_join (backend_url,
216 : path,
217 : NULL);
218 6 : GNUNET_free (path);
219 6 : if (NULL == ugh->url)
220 : {
221 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
222 : "Failed to build /private/units/%s URL\n",
223 : unit_id);
224 0 : GNUNET_free (ugh);
225 0 : return NULL;
226 : }
227 6 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
228 : "Requesting URL '%s'\n",
229 : ugh->url);
230 6 : eh = TALER_MERCHANT_curl_easy_get_ (ugh->url);
231 6 : ugh->job = GNUNET_CURL_job_add (ctx,
232 : eh,
233 : &handle_get_unit_finished,
234 : ugh);
235 6 : return ugh;
236 : }
237 :
238 :
239 : void
240 6 : TALER_MERCHANT_unit_get_cancel (struct TALER_MERCHANT_UnitGetHandle *ugh)
241 : {
242 6 : if (NULL != ugh->job)
243 0 : GNUNET_CURL_job_cancel (ugh->job);
244 6 : GNUNET_free (ugh->url);
245 6 : GNUNET_free (ugh);
246 6 : }
247 :
248 :
249 : /* end of merchant_api_get_unit.c */
|