diff options
Diffstat (limited to 'src/restclient/anastasis_api_config.c')
-rw-r--r-- | src/restclient/anastasis_api_config.c | 317 |
1 files changed, 317 insertions, 0 deletions
diff --git a/src/restclient/anastasis_api_config.c b/src/restclient/anastasis_api_config.c new file mode 100644 index 0000000..cf0846b --- /dev/null +++ b/src/restclient/anastasis_api_config.c | |||
@@ -0,0 +1,317 @@ | |||
1 | /* | ||
2 | This file is part of Anastasis | ||
3 | Copyright (C) 2020, 2021 Taler Systems SA | ||
4 | |||
5 | Anastasis 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 3, or (at your option) any later version. | ||
8 | |||
9 | Anastasis 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 General Public License for more details. | ||
12 | |||
13 | You should have received a copy of the GNU General Public License along with | ||
14 | Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> | ||
15 | */ | ||
16 | /** | ||
17 | * @file lib/anastasis_api_config.c | ||
18 | * @brief Implementation of the /config GET | ||
19 | * @author Christian Grothoff | ||
20 | * @author Dennis Neufeld | ||
21 | * @author Dominik Meister | ||
22 | */ | ||
23 | #include "platform.h" | ||
24 | #include <curl/curl.h> | ||
25 | #include <microhttpd.h> /* just for HTTP status codes */ | ||
26 | #include "anastasis_service.h" | ||
27 | #include "anastasis_api_curl_defaults.h" | ||
28 | #include <gnunet/gnunet_json_lib.h> | ||
29 | #include <taler/taler_json_lib.h> | ||
30 | |||
31 | |||
32 | /** | ||
33 | * Which version of the Taler protocol is implemented | ||
34 | * by this library? Used to determine compatibility. | ||
35 | */ | ||
36 | #define ANASTASIS_PROTOCOL_CURRENT 0 | ||
37 | |||
38 | /** | ||
39 | * How many versions are we backwards compatible with? | ||
40 | */ | ||
41 | #define ANASTASIS_PROTOCOL_AGE 0 | ||
42 | |||
43 | |||
44 | struct ANASTASIS_ConfigOperation | ||
45 | { | ||
46 | /** | ||
47 | * The url for this request. | ||
48 | */ | ||
49 | char *url; | ||
50 | |||
51 | /** | ||
52 | * Handle for the request. | ||
53 | */ | ||
54 | struct GNUNET_CURL_Job *job; | ||
55 | |||
56 | /** | ||
57 | * Reference to the execution context. | ||
58 | */ | ||
59 | struct GNUNET_CURL_Context *ctx; | ||
60 | |||
61 | /** | ||
62 | * The callback to pass the backend response to | ||
63 | */ | ||
64 | ANASTASIS_ConfigCallback cb; | ||
65 | |||
66 | /** | ||
67 | * Closure for @a cb. | ||
68 | */ | ||
69 | void *cb_cls; | ||
70 | |||
71 | }; | ||
72 | |||
73 | |||
74 | /** | ||
75 | * Function called when we're done processing the | ||
76 | * HTTP /config request. | ||
77 | * | ||
78 | * @param cls the `struct ANASTASIS_ConfigOperation` | ||
79 | * @param response_code HTTP response code, 0 on error | ||
80 | * @param response parsed JSON result, NULL on error | ||
81 | */ | ||
82 | static void | ||
83 | handle_config_finished (void *cls, | ||
84 | long response_code, | ||
85 | const void *response) | ||
86 | { | ||
87 | struct ANASTASIS_ConfigOperation *co = cls; | ||
88 | const json_t *json = response; | ||
89 | |||
90 | co->job = NULL; | ||
91 | switch (response_code) | ||
92 | { | ||
93 | case 0: | ||
94 | /* Hard error */ | ||
95 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
96 | "Backend `%s' failed to respond to GET /config\n", | ||
97 | co->url); | ||
98 | break; | ||
99 | case MHD_HTTP_OK: | ||
100 | { | ||
101 | const char *name; | ||
102 | struct ANASTASIS_Config acfg; | ||
103 | json_t *methods; | ||
104 | struct GNUNET_JSON_Specification spec[] = { | ||
105 | GNUNET_JSON_spec_string ("name", | ||
106 | &name), | ||
107 | GNUNET_JSON_spec_string ("business_name", | ||
108 | &acfg.business_name), | ||
109 | GNUNET_JSON_spec_string ("version", | ||
110 | &acfg.version), | ||
111 | GNUNET_JSON_spec_string ("currency", | ||
112 | &acfg.currency), | ||
113 | GNUNET_JSON_spec_json ("methods", | ||
114 | &methods), | ||
115 | GNUNET_JSON_spec_uint32 ("storage_limit_in_megabytes", | ||
116 | &acfg.storage_limit_in_megabytes), | ||
117 | TALER_JSON_spec_amount_any ("annual_fee", | ||
118 | &acfg.annual_fee), | ||
119 | TALER_JSON_spec_amount_any ("truth_upload_fee", | ||
120 | &acfg.truth_upload_fee), | ||
121 | TALER_JSON_spec_amount_any ("liability_limit", | ||
122 | &acfg.liability_limit), | ||
123 | GNUNET_JSON_spec_fixed_auto ("server_salt", | ||
124 | &acfg.salt), | ||
125 | GNUNET_JSON_spec_end () | ||
126 | }; | ||
127 | |||
128 | if (GNUNET_OK != | ||
129 | GNUNET_JSON_parse (json, | ||
130 | spec, | ||
131 | NULL, NULL)) | ||
132 | { | ||
133 | GNUNET_break_op (0); | ||
134 | response_code = 0; | ||
135 | break; | ||
136 | } | ||
137 | if (0 != strcmp (name, | ||
138 | "anastasis")) | ||
139 | { | ||
140 | GNUNET_JSON_parse_free (spec); | ||
141 | response_code = 0; | ||
142 | break; | ||
143 | } | ||
144 | { | ||
145 | unsigned int age; | ||
146 | unsigned int revision; | ||
147 | unsigned int current; | ||
148 | char dummy; | ||
149 | |||
150 | if (3 != sscanf (acfg.version, | ||
151 | "%u:%u:%u%c", | ||
152 | ¤t, | ||
153 | &revision, | ||
154 | &age, | ||
155 | &dummy)) | ||
156 | { | ||
157 | GNUNET_break_op (0); | ||
158 | response_code = 0; | ||
159 | GNUNET_JSON_parse_free (spec); | ||
160 | break; | ||
161 | } | ||
162 | if ( (ANASTASIS_PROTOCOL_CURRENT < current) && | ||
163 | (ANASTASIS_PROTOCOL_CURRENT < current - age) ) | ||
164 | { | ||
165 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
166 | "Provider protocol version too new\n"); | ||
167 | response_code = 0; | ||
168 | GNUNET_JSON_parse_free (spec); | ||
169 | break; | ||
170 | } | ||
171 | if ( (ANASTASIS_PROTOCOL_CURRENT > current) && | ||
172 | (ANASTASIS_PROTOCOL_CURRENT - ANASTASIS_PROTOCOL_AGE > current) ) | ||
173 | { | ||
174 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
175 | "Provider protocol version too old\n"); | ||
176 | GNUNET_break_op (0); | ||
177 | response_code = 0; | ||
178 | GNUNET_JSON_parse_free (spec); | ||
179 | break; | ||
180 | } | ||
181 | } | ||
182 | if ( (GNUNET_OK != | ||
183 | TALER_amount_cmp_currency (&acfg.liability_limit, | ||
184 | &acfg.annual_fee)) || | ||
185 | (0 != | ||
186 | strcasecmp (acfg.currency, | ||
187 | acfg.annual_fee.currency)) ) | ||
188 | { | ||
189 | GNUNET_break_op (0); | ||
190 | GNUNET_JSON_parse_free (spec); | ||
191 | response_code = 0; | ||
192 | break; | ||
193 | } | ||
194 | |||
195 | if (! json_is_array (methods)) | ||
196 | { | ||
197 | GNUNET_break_op (0); | ||
198 | GNUNET_JSON_parse_free (spec); | ||
199 | response_code = 0; | ||
200 | break; | ||
201 | } | ||
202 | acfg.methods_length = json_array_size (methods); | ||
203 | { | ||
204 | struct ANASTASIS_AuthorizationMethodConfig mcfg[GNUNET_NZL ( | ||
205 | acfg.methods_length)]; | ||
206 | |||
207 | for (unsigned int i = 0; i<acfg.methods_length; i++) | ||
208 | { | ||
209 | struct ANASTASIS_AuthorizationMethodConfig *m = &mcfg[i]; | ||
210 | struct GNUNET_JSON_Specification spec[] = { | ||
211 | GNUNET_JSON_spec_string ("type", | ||
212 | &m->type), | ||
213 | TALER_JSON_spec_amount_any ("cost", | ||
214 | &m->usage_fee), | ||
215 | GNUNET_JSON_spec_end () | ||
216 | }; | ||
217 | |||
218 | if ( (GNUNET_OK != | ||
219 | GNUNET_JSON_parse (json_array_get (methods, | ||
220 | i), | ||
221 | spec, | ||
222 | NULL, NULL)) || | ||
223 | (0 != | ||
224 | strcasecmp (m->usage_fee.currency, | ||
225 | acfg.currency)) ) | ||
226 | { | ||
227 | GNUNET_break_op (0); | ||
228 | GNUNET_JSON_parse_free (spec); | ||
229 | response_code = 0; | ||
230 | goto end; | ||
231 | } | ||
232 | } | ||
233 | acfg.methods = mcfg; | ||
234 | co->cb (co->cb_cls, | ||
235 | MHD_HTTP_OK, | ||
236 | &acfg); | ||
237 | GNUNET_JSON_parse_free (spec); | ||
238 | ANASTASIS_config_cancel (co); | ||
239 | return; | ||
240 | } | ||
241 | } | ||
242 | case MHD_HTTP_BAD_REQUEST: | ||
243 | /* This should never happen, either us or the anastasis server is buggy | ||
244 | (or API version conflict); just pass JSON reply to the application */ | ||
245 | break; | ||
246 | case MHD_HTTP_NOT_FOUND: | ||
247 | /* Nothing really to verify */ | ||
248 | break; | ||
249 | case MHD_HTTP_INTERNAL_SERVER_ERROR: | ||
250 | /* Server had an internal issue; we should retry, but this API | ||
251 | leaves this to the application */ | ||
252 | break; | ||
253 | default: | ||
254 | /* unexpected response code */ | ||
255 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
256 | "Unexpected response code %u\n", | ||
257 | (unsigned int) response_code); | ||
258 | GNUNET_break_op (0); | ||
259 | break; | ||
260 | } | ||
261 | end: | ||
262 | co->cb (co->cb_cls, | ||
263 | response_code, | ||
264 | NULL); | ||
265 | ANASTASIS_config_cancel (co); | ||
266 | } | ||
267 | |||
268 | |||
269 | struct ANASTASIS_ConfigOperation * | ||
270 | ANASTASIS_get_config (struct GNUNET_CURL_Context *ctx, | ||
271 | const char *base_url, | ||
272 | ANASTASIS_ConfigCallback cb, | ||
273 | void *cb_cls) | ||
274 | { | ||
275 | struct ANASTASIS_ConfigOperation *co; | ||
276 | |||
277 | co = GNUNET_new (struct ANASTASIS_ConfigOperation); | ||
278 | co->url = TALER_url_join (base_url, | ||
279 | "config", | ||
280 | NULL); | ||
281 | co->ctx = ctx; | ||
282 | co->cb = cb; | ||
283 | co->cb_cls = cb_cls; | ||
284 | { | ||
285 | CURL *eh; | ||
286 | |||
287 | eh = ANASTASIS_curl_easy_get_ (co->url); | ||
288 | co->job = GNUNET_CURL_job_add2 (ctx, | ||
289 | eh, | ||
290 | GNUNET_NO, | ||
291 | &handle_config_finished, | ||
292 | co); | ||
293 | } | ||
294 | if (NULL == co->job) | ||
295 | { | ||
296 | GNUNET_free (co->url); | ||
297 | GNUNET_free (co); | ||
298 | return NULL; | ||
299 | } | ||
300 | return co; | ||
301 | } | ||
302 | |||
303 | |||
304 | void | ||
305 | ANASTASIS_config_cancel (struct ANASTASIS_ConfigOperation *co) | ||
306 | { | ||
307 | if (NULL != co->job) | ||
308 | { | ||
309 | GNUNET_CURL_job_cancel (co->job); | ||
310 | co->job = NULL; | ||
311 | } | ||
312 | GNUNET_free (co->url); | ||
313 | GNUNET_free (co); | ||
314 | } | ||
315 | |||
316 | |||
317 | /* end of anastasis_api_config.c */ | ||