sync

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

sync_api_download.c (8337B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2014-2019 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify
      6   it under the terms of the GNU Lesser General Public License as
      7   published by the Free Software Foundation; either version 2.1,
      8   or (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 Lesser General Public License for more details.
     14 
     15   You should have received a copy of the GNU Lesser General Public
     16   License along with TALER; see the file COPYING.LGPL.  If not,
     17   see <http://www.gnu.org/licenses/>
     18 */
     19 
     20 /**
     21  * @file lib/sync_api_download.c
     22  * @brief Implementation of the download
     23  * @author Christian Grothoff
     24  */
     25 #include "platform.h"
     26 #include <curl/curl.h>
     27 #include <jansson.h>
     28 #include <microhttpd.h> /* just for HTTP status codes */
     29 #include <gnunet/gnunet_util_lib.h>
     30 #include <gnunet/gnunet_curl_lib.h>
     31 #include <taler/taler_signatures.h>
     32 #include "sync_service.h"
     33 #include "sync_api_curl_defaults.h"
     34 
     35 
     36 /**
     37  * @brief Handle for a download operation.
     38  */
     39 struct SYNC_DownloadOperation
     40 {
     41 
     42   /**
     43    * The url for this request.
     44    */
     45   char *url;
     46 
     47   /**
     48    * Handle for the request.
     49    */
     50   struct GNUNET_CURL_Job *job;
     51 
     52   /**
     53    * Reference to the execution context.
     54    */
     55   struct GNUNET_CURL_Context *ctx;
     56 
     57   /**
     58    * Function to call with the result.
     59    */
     60   SYNC_DownloadCallback cb;
     61 
     62   /**
     63    * Closure for @e cb.
     64    */
     65   void *cb_cls;
     66 
     67   /**
     68    * Public key of the account we are downloading from.
     69    */
     70   struct SYNC_AccountPublicKeyP account_pub;
     71 
     72   /**
     73    * Signature returned in the "Sync-Signature"
     74    * header, or all zeros for none.
     75    */
     76   struct SYNC_AccountSignatureP account_sig;
     77 
     78   /**
     79    * Hash code returned by the server in the
     80    * "Sync-Previous" header, or all zeros for
     81    * none.
     82    */
     83   struct GNUNET_HashCode sync_previous;
     84 
     85 };
     86 
     87 
     88 /**
     89  * Function called when we're done processing the
     90  * HTTP /backup request.
     91  *
     92  * @param cls the `struct SYNC_DownloadOperation`
     93  * @param response_code HTTP response code, 0 on error
     94  * @param data data we downloaded
     95  * @param data_size number of bytes in @a data
     96  */
     97 static void
     98 handle_download_finished (void *cls,
     99                           long response_code,
    100                           const void *data,
    101                           size_t data_size)
    102 {
    103   struct SYNC_DownloadOperation *download = cls;
    104   struct SYNC_DownloadDetails dd = {
    105     .http_status = (unsigned int) response_code
    106   };
    107 
    108   download->job = NULL;
    109   switch (response_code)
    110   {
    111   case 0:
    112     break;
    113   case MHD_HTTP_OK:
    114     {
    115       struct SYNC_UploadSignaturePS usp = {
    116         .purpose.purpose = htonl (TALER_SIGNATURE_SYNC_BACKUP_UPLOAD),
    117         .purpose.size = htonl (sizeof (usp)),
    118         .old_backup_hash = download->sync_previous
    119       };
    120 
    121       GNUNET_CRYPTO_hash (data,
    122                           data_size,
    123                           &usp.new_backup_hash);
    124       if (GNUNET_OK !=
    125           GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_SYNC_BACKUP_UPLOAD,
    126                                       &usp,
    127                                       &download->account_sig.eddsa_sig,
    128                                       &download->account_pub.eddsa_pub))
    129       {
    130         GNUNET_break_op (0);
    131         dd.http_status = 0;
    132         break;
    133       }
    134       /* Success, call callback with all details! */
    135       dd.details.ok.sig = download->account_sig;
    136       dd.details.ok.prev_backup_hash = download->sync_previous;
    137       dd.details.ok.curr_backup_hash = usp.new_backup_hash;
    138       dd.details.ok.backup = data;
    139       dd.details.ok.backup_size = data_size;
    140       download->cb (download->cb_cls,
    141                     &dd);
    142       download->cb = NULL;
    143       SYNC_download_cancel (download);
    144       return;
    145     }
    146   case MHD_HTTP_BAD_REQUEST:
    147     /* This should never happen, either us or the sync server is buggy
    148        (or API version conflict); just pass JSON reply to the application */
    149     break;
    150   case MHD_HTTP_NOT_FOUND:
    151     /* Nothing really to verify */
    152     break;
    153   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    154     /* Server had an internal issue; we should retry, but this API
    155        leaves this to the application */
    156     break;
    157   default:
    158     /* unexpected response code */
    159     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    160                 "Unexpected response code %u\n",
    161                 (unsigned int) response_code);
    162     GNUNET_break (0);
    163     dd.http_status = 0;
    164     break;
    165   }
    166   if (NULL != download->cb)
    167   {
    168     download->cb (download->cb_cls,
    169                   &dd);
    170     download->cb = NULL;
    171   }
    172   SYNC_download_cancel (download);
    173 }
    174 
    175 
    176 /**
    177  * Handle HTTP header received by curl.
    178  *
    179  * @param buffer one line of HTTP header data
    180  * @param size size of an item
    181  * @param nitems number of items passed
    182  * @param userdata our `struct SYNC_DownloadOperation *`
    183  * @return `size * nitems`
    184  */
    185 static size_t
    186 handle_header (char *buffer,
    187                size_t size,
    188                size_t nitems,
    189                void *userdata)
    190 {
    191   struct SYNC_DownloadOperation *download = userdata;
    192   size_t total = size * nitems;
    193   char *ndup;
    194   const char *hdr_type;
    195   char *hdr_val;
    196   char *sp;
    197 
    198   ndup = GNUNET_strndup (buffer,
    199                          total);
    200   hdr_type = strtok_r (ndup,
    201                        ":",
    202                        &sp);
    203   if (NULL == hdr_type)
    204   {
    205     GNUNET_free (ndup);
    206     return total;
    207   }
    208   hdr_val = strtok_r (NULL,
    209                       "\n\r",
    210                       &sp);
    211   if (NULL == hdr_val)
    212   {
    213     GNUNET_free (ndup);
    214     return total;
    215   }
    216   if (' ' == *hdr_val)
    217     hdr_val++;
    218   if (0 == strcasecmp (hdr_type,
    219                        "Sync-Signature"))
    220   {
    221     if (GNUNET_OK !=
    222         GNUNET_STRINGS_string_to_data (hdr_val,
    223                                        strlen (hdr_val),
    224                                        &download->account_sig,
    225                                        sizeof (struct SYNC_AccountSignatureP)))
    226     {
    227       GNUNET_break_op (0);
    228       GNUNET_free (ndup);
    229       return 0;
    230     }
    231   }
    232   if (0 == strcasecmp (hdr_type,
    233                        "Sync-Previous"))
    234   {
    235     if (GNUNET_OK !=
    236         GNUNET_STRINGS_string_to_data (hdr_val,
    237                                        strlen (hdr_val),
    238                                        &download->sync_previous,
    239                                        sizeof (struct GNUNET_HashCode)))
    240     {
    241       GNUNET_break_op (0);
    242       GNUNET_free (ndup);
    243       return 0;
    244     }
    245   }
    246   GNUNET_free (ndup);
    247   return total;
    248 }
    249 
    250 
    251 struct SYNC_DownloadOperation *
    252 SYNC_download (struct GNUNET_CURL_Context *ctx,
    253                const char *base_url,
    254                const struct SYNC_AccountPublicKeyP *pub,
    255                SYNC_DownloadCallback cb,
    256                void *cb_cls)
    257 {
    258   struct SYNC_DownloadOperation *download;
    259   char *pub_str;
    260   CURL *eh;
    261 
    262   download = GNUNET_new (struct SYNC_DownloadOperation);
    263   download->account_pub = *pub;
    264   pub_str = GNUNET_STRINGS_data_to_string_alloc (pub,
    265                                                  sizeof (*pub));
    266   GNUNET_asprintf (&download->url,
    267                    "%s%sbackups/%s",
    268                    base_url,
    269                    '/' == base_url[strlen (base_url) - 1]
    270                    ? ""
    271                    : "/",
    272                    pub_str);
    273   GNUNET_free (pub_str);
    274   eh = SYNC_curl_easy_get_ (download->url);
    275   GNUNET_assert (CURLE_OK ==
    276                  curl_easy_setopt (eh,
    277                                    CURLOPT_HEADERFUNCTION,
    278                                    &handle_header));
    279   GNUNET_assert (CURLE_OK ==
    280                  curl_easy_setopt (eh,
    281                                    CURLOPT_HEADERDATA,
    282                                    download));
    283   download->cb = cb;
    284   download->cb_cls = cb_cls;
    285   download->job = GNUNET_CURL_job_add_raw (ctx,
    286                                            eh,
    287                                            NULL,
    288                                            &handle_download_finished,
    289                                            download);
    290   return download;
    291 }
    292 
    293 
    294 void
    295 SYNC_download_cancel (struct SYNC_DownloadOperation *download)
    296 {
    297   if (NULL != download->job)
    298   {
    299     GNUNET_CURL_job_cancel (download->job);
    300     download->job = NULL;
    301   }
    302   GNUNET_free (download->url);
    303   GNUNET_free (download);
    304 }
    305 
    306 
    307 /* end of sync_api_download.c */