anastasis_api_config.c (8637B)
1 /* 2 This file is part of Anastasis 3 Copyright (C) 2020, 2021 Anastasis SARL 4 5 Anastasis 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 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 restclient/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 struct ANASTASIS_Config acfg = { 90 .http_status = response_code, 91 .response = json 92 }; 93 94 co->job = NULL; 95 switch (response_code) 96 { 97 case 0: 98 /* No reply received */ 99 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 100 "Backend `%s' failed to respond to GET /config\n", 101 co->url); 102 break; 103 case MHD_HTTP_OK: 104 { 105 const char *name; 106 const json_t *methods; 107 struct TALER_JSON_ProtocolVersion pv; 108 struct GNUNET_JSON_Specification spec[] = { 109 GNUNET_JSON_spec_string ("name", 110 &name), 111 GNUNET_JSON_spec_string ("business_name", 112 &acfg.details.ok.business_name), 113 GNUNET_JSON_spec_string ("version", 114 &acfg.details.ok.version), 115 TALER_JSON_spec_version ("version", 116 &pv), 117 GNUNET_JSON_spec_array_const ("methods", 118 &methods), 119 GNUNET_JSON_spec_uint32 ("storage_limit_in_megabytes", 120 &acfg.details.ok.storage_limit_in_megabytes), 121 TALER_JSON_spec_amount_any ("annual_fee", 122 &acfg.details.ok.annual_fee), 123 TALER_JSON_spec_amount_any ("truth_upload_fee", 124 &acfg.details.ok.truth_upload_fee), 125 TALER_JSON_spec_amount_any ("liability_limit", 126 &acfg.details.ok.liability_limit), 127 GNUNET_JSON_spec_fixed_auto ("provider_salt", 128 &acfg.details.ok.provider_salt), 129 GNUNET_JSON_spec_end () 130 }; 131 132 if (GNUNET_OK != 133 GNUNET_JSON_parse (json, 134 spec, 135 NULL, NULL)) 136 { 137 GNUNET_break_op (0); 138 json_dumpf (json, 139 stderr, 140 JSON_INDENT (2)); 141 acfg.http_status = 0; 142 acfg.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 143 break; 144 } 145 if (0 != strcmp (name, 146 "anastasis")) 147 { 148 GNUNET_JSON_parse_free (spec); 149 acfg.http_status = 0; 150 acfg.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 151 break; 152 } 153 if ( (ANASTASIS_PROTOCOL_CURRENT < pv.current) && 154 (ANASTASIS_PROTOCOL_CURRENT < pv.current - pv.age) ) 155 { 156 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 157 "Provider protocol version too new\n"); 158 acfg.http_status = 0; 159 acfg.ec = TALER_EC_GENERIC_VERSION_MALFORMED; 160 break; 161 } 162 if ( (ANASTASIS_PROTOCOL_CURRENT > pv.current) && 163 (ANASTASIS_PROTOCOL_CURRENT - ANASTASIS_PROTOCOL_AGE > pv.current) ) 164 { 165 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 166 "Provider protocol version too old\n"); 167 GNUNET_break_op (0); 168 acfg.http_status = 0; 169 acfg.ec = TALER_EC_GENERIC_VERSION_MALFORMED; 170 break; 171 } 172 acfg.details.ok.methods_length = (unsigned int) json_array_size (methods); 173 if (((size_t) acfg.details.ok.methods_length) != 174 json_array_size (methods)) 175 { 176 GNUNET_break_op (0); 177 acfg.http_status = 0; 178 acfg.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 179 break; 180 } 181 { 182 struct ANASTASIS_AuthorizationMethodConfig mcfg[ 183 GNUNET_NZL (acfg.details.ok.methods_length)]; 184 185 for (unsigned int i = 0; i<acfg.details.ok.methods_length; i++) 186 { 187 struct ANASTASIS_AuthorizationMethodConfig *m = &mcfg[i]; 188 struct GNUNET_JSON_Specification ispec[] = { 189 GNUNET_JSON_spec_string ("type", 190 &m->type), 191 TALER_JSON_spec_amount_any ("cost", 192 &m->usage_fee), 193 GNUNET_JSON_spec_end () 194 }; 195 196 if ( (GNUNET_OK != 197 GNUNET_JSON_parse (json_array_get (methods, 198 i), 199 ispec, 200 NULL, NULL)) ) 201 { 202 GNUNET_break_op (0); 203 acfg.http_status = 0; 204 acfg.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 205 goto end; 206 } 207 } 208 acfg.details.ok.methods = mcfg; 209 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 210 "Good backend found at `%s'\n", 211 co->url); 212 co->cb (co->cb_cls, 213 &acfg); 214 ANASTASIS_config_cancel (co); 215 return; 216 } 217 } 218 case MHD_HTTP_BAD_REQUEST: 219 /* This should never happen, either us or the anastasis server is buggy 220 (or API version conflict); just pass JSON reply to the application */ 221 break; 222 case MHD_HTTP_NOT_FOUND: 223 /* Nothing really to verify */ 224 break; 225 case MHD_HTTP_INTERNAL_SERVER_ERROR: 226 /* Server had an internal issue; we should retry, but this API 227 leaves this to the application */ 228 break; 229 default: 230 /* unexpected response code */ 231 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 232 "Unexpected response code %u\n", 233 (unsigned int) response_code); 234 GNUNET_break_op (0); 235 break; 236 } 237 end: 238 co->cb (co->cb_cls, 239 &acfg); 240 ANASTASIS_config_cancel (co); 241 } 242 243 244 struct ANASTASIS_ConfigOperation * 245 ANASTASIS_get_config (struct GNUNET_CURL_Context *ctx, 246 const char *base_url, 247 ANASTASIS_ConfigCallback cb, 248 void *cb_cls) 249 { 250 struct ANASTASIS_ConfigOperation *co; 251 252 co = GNUNET_new (struct ANASTASIS_ConfigOperation); 253 co->url = TALER_url_join (base_url, 254 "config", 255 NULL); 256 co->ctx = ctx; 257 co->cb = cb; 258 co->cb_cls = cb_cls; 259 { 260 CURL *eh; 261 262 eh = ANASTASIS_curl_easy_get_ (co->url); 263 co->job = GNUNET_CURL_job_add (ctx, 264 eh, 265 &handle_config_finished, 266 co); 267 } 268 if (NULL == co->job) 269 { 270 GNUNET_free (co->url); 271 GNUNET_free (co); 272 return NULL; 273 } 274 return co; 275 } 276 277 278 void 279 ANASTASIS_config_cancel (struct ANASTASIS_ConfigOperation *co) 280 { 281 if (NULL != co->job) 282 { 283 GNUNET_CURL_job_cancel (co->job); 284 co->job = NULL; 285 } 286 GNUNET_free (co->url); 287 GNUNET_free (co); 288 } 289 290 291 /* end of anastasis_api_config.c */