sync

Backup service to store encrypted wallet databases (experimental)
Log | Files | Refs | Submodules | README | LICENSE

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 */