frosix_api_config.c (8198B)
1 /* 2 This file is part of Frosix 3 Copyright (C) 2020, 2021 Anastasis SARL 4 5 Frosix is free software; you can redistribute it and/or modify it under the 6 terms of the GNU General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 Frosix 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 Frosix; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file restclient/frosix_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 "frosix_service.h" 27 #include "frosix_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 FROSIX_PROTOCOL_CURRENT 0 37 38 /** 39 * How many versions are we backwards compatible with? 40 */ 41 #define FROSIX_PROTOCOL_AGE 0 42 43 44 45 /** 46 * Function called when we're done processing the 47 * HTTP /config request. 48 * 49 * @param cls the `struct FROSIX_ConfigOperation` 50 * @param response_code HTTP response code, 0 on error 51 * @param response parsed JSON result, NULL on error 52 */ 53 static void 54 handle_config_finished (void *cls, 55 long response_code, 56 const void *response) 57 { 58 struct FROSIX_ConfigOperation *co = cls; 59 const json_t *json = response; 60 61 co->job = NULL; 62 63 switch (response_code) 64 { 65 case 0: 66 /* No reply received */ 67 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 68 "Backend `%s' failed to respond to GET /config\n", 69 co->url); 70 break; 71 case MHD_HTTP_OK: 72 { 73 const char *name; 74 struct FROSIX_Config fcfg; 75 fcfg.provider_index = co->provider_index; 76 json_t *methods; 77 struct GNUNET_JSON_Specification spec[] = { 78 GNUNET_JSON_spec_string ("name", 79 &name), 80 GNUNET_JSON_spec_string ("business_name", 81 &fcfg.business_name), 82 GNUNET_JSON_spec_string ("version", 83 &fcfg.version), 84 GNUNET_JSON_spec_json ("methods", 85 &methods), 86 TALER_JSON_spec_amount_any ("annual_fee", 87 &fcfg.annual_fee), 88 TALER_JSON_spec_amount_any ("signature_creation_fee", 89 &fcfg.signature_creation_fee), 90 GNUNET_JSON_spec_fixed_auto ("provider_salt", 91 &fcfg.provider_salt), 92 GNUNET_JSON_spec_fixed_auto ("public_key", 93 &fcfg.public_key), 94 GNUNET_JSON_spec_end () 95 }; 96 97 if (GNUNET_OK != 98 GNUNET_JSON_parse (json, 99 spec, 100 NULL, NULL)) 101 { 102 GNUNET_break_op (0); 103 json_dumpf (json, 104 stderr, 105 JSON_INDENT (2)); 106 response_code = 0; 107 break; 108 } 109 if (0 != strcmp (name, 110 "frosix")) 111 { 112 GNUNET_JSON_parse_free (spec); 113 response_code = 0; 114 break; 115 } 116 { 117 unsigned int age; 118 unsigned int revision; 119 unsigned int current; 120 char dummy; 121 122 if (3 != sscanf (fcfg.version, 123 "%u:%u:%u%c", 124 ¤t, 125 &revision, 126 &age, 127 &dummy)) 128 { 129 GNUNET_break_op (0); 130 response_code = 0; 131 GNUNET_JSON_parse_free (spec); 132 break; 133 } 134 if ( (FROSIX_PROTOCOL_CURRENT < current) && 135 (FROSIX_PROTOCOL_CURRENT < current - age) ) 136 { 137 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 138 "Provider protocol version too new\n"); 139 response_code = 0; 140 GNUNET_JSON_parse_free (spec); 141 break; 142 } 143 if ( (FROSIX_PROTOCOL_CURRENT > current) && 144 (FROSIX_PROTOCOL_CURRENT - FROSIX_PROTOCOL_AGE > current) ) 145 { 146 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 147 "Provider protocol version too old\n"); 148 GNUNET_break_op (0); 149 response_code = 0; 150 GNUNET_JSON_parse_free (spec); 151 break; 152 } 153 } 154 if (! json_is_array (methods)) 155 { 156 GNUNET_break_op (0); 157 GNUNET_JSON_parse_free (spec); 158 response_code = 0; 159 break; 160 } 161 fcfg.methods_length = json_array_size (methods); 162 { 163 struct FROSIX_AuthorizationMethodConfig mcfg[GNUNET_NZL ( 164 fcfg.methods_length)]; 165 166 for (unsigned int i = 0; i<fcfg.methods_length; i++) 167 { 168 struct FROSIX_AuthorizationMethodConfig *m = &mcfg[i]; 169 struct GNUNET_JSON_Specification spec[] = { 170 GNUNET_JSON_spec_string ("type", 171 &m->type), 172 TALER_JSON_spec_amount_any ("cost", 173 &m->usage_fee), 174 GNUNET_JSON_spec_end () 175 }; 176 177 if ( (GNUNET_OK != 178 GNUNET_JSON_parse (json_array_get (methods, 179 i), 180 spec, 181 NULL, NULL)) ) 182 { 183 GNUNET_break_op (0); 184 GNUNET_JSON_parse_free (spec); 185 response_code = 0; 186 goto end; 187 } 188 } 189 fcfg.methods = mcfg; 190 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 191 "Good backend found at `%s'\n", 192 co->url); 193 co->cb (co->cb_cls, 194 MHD_HTTP_OK, 195 &fcfg); 196 GNUNET_JSON_parse_free (spec); 197 FROSIX_config_cancel (co); 198 return; 199 } 200 } 201 case MHD_HTTP_BAD_REQUEST: 202 /* This should never happen, either us or the anastasis server is buggy 203 (or API version conflict); just pass JSON reply to the application */ 204 break; 205 case MHD_HTTP_NOT_FOUND: 206 /* Nothing really to verify */ 207 break; 208 case MHD_HTTP_INTERNAL_SERVER_ERROR: 209 /* Server had an internal issue; we should retry, but this API 210 leaves this to the application */ 211 break; 212 default: 213 /* unexpected response code */ 214 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 215 "Unexpected response code %u\n", 216 (unsigned int) response_code); 217 GNUNET_break_op (0); 218 break; 219 } 220 end: 221 co->cb (co->cb_cls, 222 response_code, 223 NULL); 224 FROSIX_config_cancel (co); 225 } 226 227 228 struct FROSIX_ConfigOperation * 229 FROSIX_get_config (struct GNUNET_CURL_Context *ctx, 230 const char *base_url, 231 uint8_t provider_index, 232 FROSIX_ConfigCallback cb, 233 void *cb_cls) 234 { 235 struct FROSIX_ConfigOperation *co; 236 237 co = GNUNET_new (struct FROSIX_ConfigOperation); 238 co->url = TALER_url_join (base_url, 239 "config", 240 NULL); 241 co->provider_index = provider_index; 242 co->ctx = ctx; 243 co->cb = cb; 244 co->cb_cls = cb_cls; 245 { 246 CURL *eh; 247 eh = FROSIX_curl_easy_get_ (co->url); 248 co->job = GNUNET_CURL_job_add (ctx, 249 eh, 250 &handle_config_finished, 251 co); 252 } 253 254 if (NULL == co->job) 255 { 256 GNUNET_free (co->url); 257 GNUNET_free (co); 258 return NULL; 259 } 260 261 return co; 262 } 263 264 265 void 266 FROSIX_config_cancel (struct FROSIX_ConfigOperation *co) 267 { 268 if (NULL != co->job) 269 { 270 GNUNET_CURL_job_cancel (co->job); 271 co->job = NULL; 272 } 273 GNUNET_free (co->url); 274 GNUNET_free (co); 275 } 276 277 278 /* end of frosix_api_config.c */