sync_api_block_reconcile.c (8720B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2026 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as 7 published by the Free Software Foundation; either version 3, or 8 (at your option) any later version. 9 10 TALER is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public 16 License along with TALER; see the file COPYING. If not, see 17 <http://www.gnu.org/licenses/> 18 */ 19 /** 20 * @file lib/sync_api_block_reconcile.c 21 * @brief Implementation of the block reconcile GET 22 * @author Iván Ávalos 23 */ 24 #include "platform.h" 25 #include <curl/curl.h> 26 #include <jansson.h> 27 #include <microhttpd2.h> 28 #include <gnunet/gnunet_util_lib.h> 29 #include <gnunet/gnunet_curl_lib.h> 30 #include <gnunet/gnunet_json_lib.h> 31 #include <taler/taler_json_lib.h> 32 #include <taler/taler_curl_lib.h> 33 #include <sync/sync_service.h> 34 #include <sync/sync_util.h> 35 #include <sync/sync_ibf.h> 36 #include "sync_api_curl_defaults.h" 37 38 39 /** 40 * Filter format version accepted by this client. 41 */ 42 #define SYNC_RECONCILE_VERSION 2 43 44 45 /** 46 * Handle for a block reconcile operation. 47 */ 48 struct SYNC_ReconcileOperation 49 { 50 51 /** 52 * The url for this request. 53 */ 54 char *url; 55 56 /** 57 * Handle for the request. 58 */ 59 struct GNUNET_CURL_Job *job; 60 61 /** 62 * Reference to the execution context. 63 */ 64 struct GNUNET_CURL_Context *ctx; 65 66 /** 67 * Function to call with the result. 68 */ 69 SYNC_ReconcileCallback cb; 70 71 /** 72 * Closure for @e cb. 73 */ 74 void *cb_cls; 75 76 }; 77 78 79 /** 80 * Function called when we're done processing the 81 * GET /backups/$KEY/blocks/reconcile request. 82 * 83 * @param cls the `struct SYNC_ReconcileOperation *` 84 * @param response_code HTTP response code, 0 on error 85 * @param response response body as parsed JSON, or NULL 86 */ 87 static void 88 handle_reconcile_finished (void *cls, 89 long response_code, 90 const void *response) 91 { 92 struct SYNC_ReconcileOperation *op = cls; 93 const json_t *json = response; 94 struct SYNC_ReconcileDetails rd = { 95 .http_status = (unsigned int) response_code, 96 .ec = TALER_EC_INVALID 97 }; 98 99 op->job = NULL; 100 switch (response_code) 101 { 102 case 0: 103 break; 104 case MHD_HTTP_STATUS_OK: 105 if (NULL != json) 106 { 107 const json_t *j; 108 const char *filter_s; 109 size_t filter_len; 110 111 /* version */ 112 j = json_object_get (json, 113 "version"); 114 if ( (NULL == j) || 115 (! json_is_integer (j)) || 116 (SYNC_RECONCILE_VERSION != json_integer_value (j)) ) 117 { 118 GNUNET_break_op (0); 119 rd.ec = TALER_EC_GENERIC_PARAMETER_MALFORMED; 120 break; 121 } 122 rd.version = (uint32_t) json_integer_value (j); 123 /* bucket_count */ 124 j = json_object_get (json, 125 "bucket_count"); 126 if ( (NULL == j) || 127 (! json_is_integer (j)) || 128 (json_integer_value (j) <= 0) ) 129 { 130 GNUNET_break_op (0); 131 rd.ec = TALER_EC_GENERIC_PARAMETER_MALFORMED; 132 break; 133 } 134 rd.bucket_count = (uint32_t) json_integer_value (j); 135 /* k */ 136 j = json_object_get (json, 137 "k"); 138 if ( (NULL == j) || 139 (! json_is_integer (j)) || 140 (json_integer_value (j) <= 0) ) 141 { 142 GNUNET_break_op (0); 143 rd.ec = TALER_EC_GENERIC_PARAMETER_MALFORMED; 144 break; 145 } 146 rd.k = (unsigned int) json_integer_value (j); 147 /* prefix */ 148 j = json_object_get (json, 149 "prefix"); 150 if (NULL == j) 151 { 152 GNUNET_break_op (0); 153 rd.ec = TALER_EC_GENERIC_PARAMETER_MALFORMED; 154 break; 155 } 156 { 157 const char *prefix_s = json_string_value (j); 158 uint32_t prefix; 159 160 if ( (NULL == prefix_s) || 161 (GNUNET_OK != 162 GNUNET_STRINGS_string_to_data (prefix_s, 163 strlen (prefix_s), 164 &prefix, 165 sizeof (prefix))) ) 166 { 167 GNUNET_break_op (0); 168 rd.ec = TALER_EC_GENERIC_PARAMETER_MALFORMED; 169 break; 170 } 171 rd.prefix = ntohl (prefix); 172 } 173 /* total_blocks */ 174 j = json_object_get (json, 175 "total_blocks"); 176 if ( (NULL == j) || 177 (! json_is_integer (j)) || 178 (json_integer_value (j) < 0) ) 179 { 180 GNUNET_break_op (0); 181 rd.ec = TALER_EC_GENERIC_PARAMETER_MALFORMED; 182 break; 183 } 184 rd.total_blocks = (uint32_t) json_integer_value (j); 185 /* filter */ 186 j = json_object_get (json, 187 "filter"); 188 if (NULL == j) 189 { 190 GNUNET_break_op (0); 191 rd.ec = TALER_EC_GENERIC_PARAMETER_MALFORMED; 192 break; 193 } 194 filter_s = json_string_value (j); 195 if (NULL == filter_s) 196 { 197 GNUNET_break_op (0); 198 rd.ec = TALER_EC_GENERIC_PARAMETER_MALFORMED; 199 break; 200 } 201 rd.data_size = ((size_t) rd.bucket_count) * SYNC_IBF_BUCKET_SIZE; 202 rd.data = GNUNET_malloc (rd.data_size); 203 filter_len = strlen (filter_s); 204 if (GNUNET_OK != 205 GNUNET_STRINGS_string_to_data (filter_s, 206 filter_len, 207 rd.data, 208 rd.data_size)) 209 { 210 GNUNET_free (rd.data); 211 rd.data = NULL; 212 rd.data_size = 0; 213 GNUNET_break_op (0); 214 rd.ec = TALER_EC_GENERIC_PARAMETER_MALFORMED; 215 break; 216 } 217 rd.ec = TALER_EC_NONE; 218 } 219 else 220 { 221 rd.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 222 } 223 break; 224 case MHD_HTTP_STATUS_BAD_REQUEST: 225 case MHD_HTTP_STATUS_NOT_FOUND: 226 case MHD_HTTP_STATUS_PAYMENT_REQUIRED: 227 case MHD_HTTP_STATUS_INTERNAL_SERVER_ERROR: 228 default: 229 if ( (response_code >= 400) && 230 (response_code < 500) ) 231 rd.ec = (NULL != json) 232 ? TALER_JSON_get_error_code (json) 233 : TALER_EC_GENERIC_PARAMETER_MALFORMED; 234 else if (response_code >= 500) 235 rd.ec = (NULL != json) 236 ? TALER_JSON_get_error_code (json) 237 : TALER_EC_GENERIC_INVALID_RESPONSE; 238 break; 239 } 240 241 if (NULL != op->cb) 242 { 243 op->cb (op->cb_cls, 244 &rd); 245 op->cb = NULL; 246 } 247 GNUNET_free (rd.data); 248 SYNC_block_reconcile_cancel (op); 249 } 250 251 252 struct SYNC_ReconcileOperation * 253 SYNC_block_reconcile (struct GNUNET_CURL_Context *ctx, 254 const char *base_url, 255 const struct SYNC_AccountPublicKeyP *pub, 256 const uint32_t *prefix, 257 SYNC_ReconcileCallback cb, 258 void *cb_cls) 259 { 260 struct SYNC_ReconcileOperation *op; 261 CURL *eh; 262 char *pub_s; 263 char *path; 264 char *url; 265 266 pub_s = GNUNET_STRINGS_data_to_string_alloc (pub, 267 sizeof (*pub)); 268 GNUNET_asprintf (&path, 269 "backups/%s/blocks/reconcile", 270 pub_s); 271 GNUNET_free (pub_s); 272 if (NULL != prefix) 273 { 274 uint32_t prefix_be = htonl (*prefix); 275 char *prefix_s; 276 277 prefix_s = GNUNET_STRINGS_data_to_string_alloc (&prefix_be, 278 sizeof (prefix_be)); 279 url = TALER_url_join (base_url, 280 path, 281 "prefix", 282 prefix_s, 283 NULL); 284 GNUNET_free (prefix_s); 285 } 286 else 287 { 288 url = TALER_url_join (base_url, 289 path, 290 NULL); 291 } 292 GNUNET_free (path); 293 if (NULL == url) 294 { 295 GNUNET_break (0); 296 return NULL; 297 } 298 299 op = GNUNET_new (struct SYNC_ReconcileOperation); 300 op->ctx = ctx; 301 op->cb = cb; 302 op->cb_cls = cb_cls; 303 op->url = url; 304 305 eh = SYNC_curl_easy_get_ (op->url); 306 if (NULL == eh) 307 { 308 GNUNET_break (0); 309 GNUNET_free (op->url); 310 GNUNET_free (op); 311 return NULL; 312 } 313 314 op->job = GNUNET_CURL_job_add2 (ctx, 315 eh, 316 NULL, 317 &handle_reconcile_finished, 318 op); 319 return op; 320 } 321 322 323 void 324 SYNC_block_reconcile_cancel (struct SYNC_ReconcileOperation *op) 325 { 326 if (NULL != op->job) 327 { 328 GNUNET_CURL_job_cancel (op->job); 329 op->job = NULL; 330 } 331 GNUNET_free (op->url); 332 GNUNET_free (op); 333 } 334 335 336 /* end of sync_api_block_reconcile.c */