/* This file is part of TALER Copyright (C) 2019 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. TALER is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with TALER; see the file COPYING. If not, see */ /** * @file sync-httpd_backup.c * @brief functions to handle incoming requests for backups * @author Christian Grothoff */ #include "platform.h" #include "sync-httpd.h" #include #include "sync-httpd_backup.h" /** * Handle request on @a connection for retrieval of the latest * backup of @a account. * * @param connection the MHD connection to handle * @param account public key of the account the request is for * @return MHD result code */ MHD_RESULT SH_backup_get (struct MHD_Connection *connection, const struct SYNC_AccountPublicKeyP *account) { struct GNUNET_HashCode backup_hash; enum SYNC_DB_QueryStatus qs; MHD_RESULT ret; qs = db->lookup_account_TR (db->cls, account, &backup_hash); switch (qs) { case SYNC_DB_OLD_BACKUP_MISSING: GNUNET_break (0); return TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, NULL); case SYNC_DB_OLD_BACKUP_MISMATCH: GNUNET_break (0); return TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, NULL); case SYNC_DB_PAYMENT_REQUIRED: return TALER_MHD_reply_with_error (connection, MHD_HTTP_NOT_FOUND, TALER_EC_SYNC_ACCOUNT_UNKNOWN, NULL); case SYNC_DB_HARD_ERROR: return TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_GENERIC_DB_FETCH_FAILED, NULL); case SYNC_DB_SOFT_ERROR: GNUNET_break (0); return TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_GENERIC_DB_SOFT_FAILURE, NULL); case SYNC_DB_NO_RESULTS: { struct MHD_Response *resp; resp = MHD_create_response_from_buffer (0, NULL, MHD_RESPMEM_PERSISTENT); TALER_MHD_add_global_headers (resp); ret = MHD_queue_response (connection, MHD_HTTP_NO_CONTENT, resp); MHD_destroy_response (resp); } return ret; case SYNC_DB_ONE_RESULT: { const char *inm; inm = MHD_lookup_connection_value (connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_IF_NONE_MATCH); if (NULL != inm) { struct GNUNET_HashCode inm_h; if (GNUNET_OK != GNUNET_STRINGS_string_to_data (inm, strlen (inm), &inm_h, sizeof (inm_h))) { GNUNET_break_op (0); return TALER_MHD_reply_with_error (connection, MHD_HTTP_BAD_REQUEST, TALER_EC_SYNC_BAD_IF_NONE_MATCH, "Etag does not include a base32-encoded SHA-512 hash"); } if (0 == GNUNET_memcmp (&inm_h, &backup_hash)) { struct MHD_Response *resp; resp = MHD_create_response_from_buffer (0, NULL, MHD_RESPMEM_PERSISTENT); TALER_MHD_add_global_headers (resp); ret = MHD_queue_response (connection, MHD_HTTP_NOT_MODIFIED, resp); MHD_destroy_response (resp); return ret; } } } /* We have a result, should fetch and return it! */ break; } return SH_return_backup (connection, account, MHD_HTTP_OK); } /** * Return the current backup of @a account on @a connection * using @a default_http_status on success. * * @param connection MHD connection to use * @param account account to query * @param default_http_status HTTP status to queue response * with on success (#MHD_HTTP_OK or #MHD_HTTP_CONFLICT) * @return MHD result code */ MHD_RESULT SH_return_backup (struct MHD_Connection *connection, const struct SYNC_AccountPublicKeyP *account, unsigned int default_http_status) { enum SYNC_DB_QueryStatus qs; struct MHD_Response *resp; MHD_RESULT ret; struct SYNC_AccountSignatureP account_sig; struct GNUNET_HashCode backup_hash; struct GNUNET_HashCode prev_hash; size_t backup_size; void *backup; qs = db->lookup_backup_TR (db->cls, account, &account_sig, &prev_hash, &backup_hash, &backup_size, &backup); switch (qs) { case SYNC_DB_OLD_BACKUP_MISSING: GNUNET_break (0); return TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, "unexpected return status (backup missing)"); case SYNC_DB_OLD_BACKUP_MISMATCH: GNUNET_break (0); return TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, "unexpected return status (backup mismatch)"); case SYNC_DB_PAYMENT_REQUIRED: GNUNET_break (0); return TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, "unexpected return status (payment required)"); case SYNC_DB_HARD_ERROR: GNUNET_break (0); return TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_GENERIC_DB_FETCH_FAILED, NULL); case SYNC_DB_SOFT_ERROR: GNUNET_break (0); return TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_GENERIC_DB_SOFT_FAILURE, NULL); case SYNC_DB_NO_RESULTS: GNUNET_break (0); /* Note: can theoretically happen due to non-transactional nature if the backup expired / was gc'ed JUST between the two SQL calls. But too rare to handle properly, as doing a transaction would be expensive. Just admit to failure ;-) */ return TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_GENERIC_DB_INVARIANT_FAILURE, NULL); case SYNC_DB_ONE_RESULT: /* interesting case below */ break; } resp = MHD_create_response_from_buffer (backup_size, backup, MHD_RESPMEM_MUST_FREE); TALER_MHD_add_global_headers (resp); { char *sig_s; char *prev_s; char *etag; sig_s = GNUNET_STRINGS_data_to_string_alloc (&account_sig, sizeof (account_sig)); prev_s = GNUNET_STRINGS_data_to_string_alloc (&prev_hash, sizeof (prev_hash)); etag = GNUNET_STRINGS_data_to_string_alloc (&backup_hash, sizeof (backup_hash)); GNUNET_break (MHD_YES == MHD_add_response_header (resp, "Sync-Signature", sig_s)); GNUNET_break (MHD_YES == MHD_add_response_header (resp, "Sync-Previous", prev_s)); GNUNET_break (MHD_YES == MHD_add_response_header (resp, MHD_HTTP_HEADER_ETAG, etag)); GNUNET_free (etag); GNUNET_free (prev_s); GNUNET_free (sig_s); } ret = MHD_queue_response (connection, default_http_status, resp); MHD_destroy_response (resp); return ret; }