sync

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

sync-httpd_backup.c (10148B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2019 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Affero General Public License as published by the Free Software
      7   Foundation; either version 3, or (at your option) any later version.
      8 
      9   TALER 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 Affero General Public License for more details.
     12 
     13   You should have received a copy of the GNU Affero General Public License along with
     14   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file sync-httpd_backup.c
     18  * @brief functions to handle incoming requests for backups
     19  * @author Christian Grothoff
     20  */
     21 #include "platform.h"
     22 #include "sync-httpd.h"
     23 #include <gnunet/gnunet_util_lib.h>
     24 #include "sync-httpd_backup.h"
     25 
     26 
     27 /**
     28  * Handle request on @a connection for retrieval of the latest
     29  * backup of @a account.
     30  *
     31  * @param connection the MHD connection to handle
     32  * @param account public key of the account the request is for
     33  * @return MHD result code
     34  */
     35 enum MHD_Result
     36 SH_backup_get (struct MHD_Connection *connection,
     37                const struct SYNC_AccountPublicKeyP *account)
     38 {
     39   struct GNUNET_HashCode backup_hash;
     40   enum SYNC_DB_QueryStatus qs;
     41   enum MHD_Result ret;
     42 
     43   qs = SYNCDB_lookup_account_TR (
     44     account,
     45     &backup_hash);
     46   switch (qs)
     47   {
     48   case SYNC_DB_OLD_BACKUP_MISSING:
     49     GNUNET_break (0);
     50     return TALER_MHD_reply_with_error (connection,
     51                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
     52                                        TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
     53                                        NULL);
     54   case SYNC_DB_OLD_BACKUP_MISMATCH:
     55     GNUNET_break (0);
     56     return TALER_MHD_reply_with_error (connection,
     57                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
     58                                        TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
     59                                        NULL);
     60   case SYNC_DB_PAYMENT_REQUIRED:
     61     return TALER_MHD_reply_with_error (connection,
     62                                        MHD_HTTP_NOT_FOUND,
     63                                        TALER_EC_SYNC_ACCOUNT_UNKNOWN,
     64                                        NULL);
     65   case SYNC_DB_HARD_ERROR:
     66     return TALER_MHD_reply_with_error (connection,
     67                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
     68                                        TALER_EC_GENERIC_DB_FETCH_FAILED,
     69                                        NULL);
     70   case SYNC_DB_SOFT_ERROR:
     71     GNUNET_break (0);
     72     return TALER_MHD_reply_with_error (connection,
     73                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
     74                                        TALER_EC_GENERIC_DB_SOFT_FAILURE,
     75                                        NULL);
     76   case SYNC_DB_NO_RESULTS:
     77     {
     78       struct MHD_Response *resp;
     79 
     80       resp = MHD_create_response_from_buffer (0,
     81                                               NULL,
     82                                               MHD_RESPMEM_PERSISTENT);
     83       TALER_MHD_add_global_headers (resp,
     84                                     false);
     85       ret = MHD_queue_response (connection,
     86                                 MHD_HTTP_NO_CONTENT,
     87                                 resp);
     88       MHD_destroy_response (resp);
     89     }
     90     return ret;
     91   case SYNC_DB_ONE_RESULT:
     92     {
     93       const char *inm;
     94 
     95       inm = MHD_lookup_connection_value (connection,
     96                                          MHD_HEADER_KIND,
     97                                          MHD_HTTP_HEADER_IF_NONE_MATCH);
     98       if ( (NULL != inm) &&
     99            (2 < strlen (inm)) &&
    100            ('"' == inm[0]) &&
    101            ('=' == inm[strlen (inm) - 1]) )
    102       {
    103         struct GNUNET_HashCode inm_h;
    104 
    105         if (GNUNET_OK !=
    106             GNUNET_STRINGS_string_to_data (inm + 1,
    107                                            strlen (inm) - 2,
    108                                            &inm_h,
    109                                            sizeof (inm_h)))
    110         {
    111           GNUNET_break_op (0);
    112           return TALER_MHD_reply_with_error (connection,
    113                                              MHD_HTTP_BAD_REQUEST,
    114                                              TALER_EC_SYNC_BAD_IF_NONE_MATCH,
    115                                              "Etag does not include a base32-encoded SHA-512 hash");
    116         }
    117         if (0 == GNUNET_memcmp (&inm_h,
    118                                 &backup_hash))
    119         {
    120           struct MHD_Response *resp;
    121 
    122           resp = MHD_create_response_from_buffer (0,
    123                                                   NULL,
    124                                                   MHD_RESPMEM_PERSISTENT);
    125           TALER_MHD_add_global_headers (resp,
    126                                         false);
    127           ret = MHD_queue_response (connection,
    128                                     MHD_HTTP_NOT_MODIFIED,
    129                                     resp);
    130           MHD_destroy_response (resp);
    131           return ret;
    132         }
    133       }
    134     }
    135     /* We have a result, should fetch and return it! */
    136     break;
    137   }
    138   return SH_return_backup (connection,
    139                            account,
    140                            MHD_HTTP_OK);
    141 }
    142 
    143 
    144 /**
    145  * Return the current backup of @a account on @a connection
    146  * using @a default_http_status on success.
    147  *
    148  * @param connection MHD connection to use
    149  * @param account account to query
    150  * @param default_http_status HTTP status to queue response
    151  *  with on success (#MHD_HTTP_OK or #MHD_HTTP_CONFLICT)
    152  * @return MHD result code
    153  */
    154 enum MHD_Result
    155 SH_return_backup (struct MHD_Connection *connection,
    156                   const struct SYNC_AccountPublicKeyP *account,
    157                   unsigned int default_http_status)
    158 {
    159   enum SYNC_DB_QueryStatus qs;
    160   struct MHD_Response *resp;
    161   enum MHD_Result ret;
    162   struct SYNC_AccountSignatureP account_sig;
    163   struct GNUNET_HashCode backup_hash;
    164   struct GNUNET_HashCode prev_hash;
    165   size_t backup_size;
    166   void *backup;
    167 
    168   qs = SYNCDB_lookup_backup_TR (
    169     account,
    170     &account_sig,
    171     &prev_hash,
    172     &backup_hash,
    173     &backup_size,
    174     &backup);
    175   switch (qs)
    176   {
    177   case SYNC_DB_OLD_BACKUP_MISSING:
    178     GNUNET_break (0);
    179     return TALER_MHD_reply_with_error (connection,
    180                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
    181                                        TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
    182                                        "unexpected return status (backup missing)");
    183   case SYNC_DB_OLD_BACKUP_MISMATCH:
    184     GNUNET_break (0);
    185     return TALER_MHD_reply_with_error (connection,
    186                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
    187                                        TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
    188                                        "unexpected return status (backup mismatch)");
    189   case SYNC_DB_PAYMENT_REQUIRED:
    190     GNUNET_break (0);
    191     return TALER_MHD_reply_with_error (connection,
    192                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
    193                                        TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
    194                                        "unexpected return status (payment required)");
    195   case SYNC_DB_HARD_ERROR:
    196     GNUNET_break (0);
    197     return TALER_MHD_reply_with_error (connection,
    198                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
    199                                        TALER_EC_GENERIC_DB_FETCH_FAILED,
    200                                        NULL);
    201   case SYNC_DB_SOFT_ERROR:
    202     GNUNET_break (0);
    203     return TALER_MHD_reply_with_error (connection,
    204                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
    205                                        TALER_EC_GENERIC_DB_SOFT_FAILURE,
    206                                        NULL);
    207   case SYNC_DB_NO_RESULTS:
    208     GNUNET_break (0);
    209     /* Note: can theoretically happen due to non-transactional nature if
    210        the backup expired / was gc'ed JUST between the two SQL calls.
    211        But too rare to handle properly, as doing a transaction would be
    212        expensive. Just admit to failure ;-) */
    213     return TALER_MHD_reply_with_error (connection,
    214                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
    215                                        TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
    216                                        NULL);
    217   case SYNC_DB_ONE_RESULT:
    218     /* interesting case below */
    219     break;
    220   }
    221   resp = MHD_create_response_from_buffer (backup_size,
    222                                           backup,
    223                                           MHD_RESPMEM_MUST_FREE);
    224   TALER_MHD_add_global_headers (resp,
    225                                 false);
    226   {
    227     char *sig_s;
    228     char *prev_s;
    229     char *etag;
    230     char *etagq;
    231 
    232     sig_s = GNUNET_STRINGS_data_to_string_alloc (&account_sig,
    233                                                  sizeof (account_sig));
    234     prev_s = GNUNET_STRINGS_data_to_string_alloc (&prev_hash,
    235                                                   sizeof (prev_hash));
    236     etag = GNUNET_STRINGS_data_to_string_alloc (&backup_hash,
    237                                                 sizeof (backup_hash));
    238     GNUNET_break (MHD_YES ==
    239                   MHD_add_response_header (resp,
    240                                            "Sync-Signature",
    241                                            sig_s));
    242     GNUNET_break (MHD_YES ==
    243                   MHD_add_response_header (resp,
    244                                            "Sync-Previous",
    245                                            prev_s));
    246     GNUNET_asprintf (&etagq,
    247                      "\"%s\"",
    248                      etag);
    249     GNUNET_break (MHD_YES ==
    250                   MHD_add_response_header (resp,
    251                                            MHD_HTTP_HEADER_ETAG,
    252                                            etagq));
    253     GNUNET_free (etagq);
    254     GNUNET_free (etag);
    255     GNUNET_free (prev_s);
    256     GNUNET_free (sig_s);
    257   }
    258   ret = MHD_queue_response (connection,
    259                             default_http_status,
    260                             resp);
    261   MHD_destroy_response (resp);
    262   return ret;
    263 }