sync-httpd2_backup.c (8881B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2019--2025 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-httpd2_backup.c 18 * @brief functions to handle incoming requests for backups 19 * @author Christian Grothoff 20 */ 21 #include "platform.h" 22 #include "sync-httpd2.h" 23 #include <gnunet/gnunet_util_lib.h> 24 #include "sync-httpd2_backup.h" 25 26 27 /** 28 * Handle request on @a request for retrieval of the latest 29 * backup of @a account. 30 * 31 * @param request the MHD request to handle 32 * @param account public key of the account the request is for 33 * @return MHD action 34 */ 35 const struct MHD_Action * 36 SH_backup_get (struct MHD_Request *request, 37 const struct SYNC_AccountPublicKeyP *account) 38 { 39 struct GNUNET_HashCode backup_hash; 40 enum SYNC_DB_QueryStatus qs; 41 42 qs = SYNCDB_lookup_account_TR ( 43 account, 44 &backup_hash); 45 switch (qs) 46 { 47 case SYNC_DB_OLD_BACKUP_MISSING: 48 GNUNET_break (0); 49 return TALER_MHD2_reply_with_error (request, 50 MHD_HTTP_STATUS_INTERNAL_SERVER_ERROR, 51 TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, 52 NULL); 53 case SYNC_DB_OLD_BACKUP_MISMATCH: 54 GNUNET_break (0); 55 return TALER_MHD2_reply_with_error (request, 56 MHD_HTTP_STATUS_INTERNAL_SERVER_ERROR, 57 TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, 58 NULL); 59 case SYNC_DB_PAYMENT_REQUIRED: 60 return TALER_MHD2_reply_with_error (request, 61 MHD_HTTP_STATUS_NOT_FOUND, 62 TALER_EC_SYNC_ACCOUNT_UNKNOWN, 63 NULL); 64 case SYNC_DB_HARD_ERROR: 65 return TALER_MHD2_reply_with_error (request, 66 MHD_HTTP_STATUS_INTERNAL_SERVER_ERROR, 67 TALER_EC_GENERIC_DB_FETCH_FAILED, 68 NULL); 69 case SYNC_DB_SOFT_ERROR: 70 GNUNET_break (0); 71 return TALER_MHD2_reply_with_error (request, 72 MHD_HTTP_STATUS_INTERNAL_SERVER_ERROR, 73 TALER_EC_GENERIC_DB_SOFT_FAILURE, 74 NULL); 75 case SYNC_DB_NO_RESULTS: 76 { 77 struct MHD_Response *resp; 78 79 resp = MHD_response_from_empty (MHD_HTTP_STATUS_NO_CONTENT); 80 TALER_MHD2_add_global_headers (resp, 81 false); 82 return MHD_action_from_response (request, 83 resp); 84 } 85 case SYNC_DB_ONE_RESULT: 86 { 87 struct MHD_StringNullable inm; 88 89 if ( (MHD_YES == 90 MHD_request_get_value (request, 91 MHD_VK_HEADER, 92 MHD_HTTP_HEADER_IF_NONE_MATCH, 93 &inm)) && 94 (2 < inm.len) && 95 ('"' == inm.cstr[0]) && 96 ('=' == inm.cstr[inm.len - 1]) ) 97 { 98 struct GNUNET_HashCode inm_h; 99 100 if (GNUNET_OK != 101 GNUNET_STRINGS_string_to_data (inm.cstr + 1, 102 inm.len - 2, 103 &inm_h, 104 sizeof (inm_h))) 105 { 106 GNUNET_break_op (0); 107 return TALER_MHD2_reply_with_error (request, 108 MHD_HTTP_STATUS_BAD_REQUEST, 109 TALER_EC_SYNC_BAD_IF_NONE_MATCH, 110 "Etag does not include a base32-encoded SHA-512 hash"); 111 } 112 if (0 == GNUNET_memcmp (&inm_h, 113 &backup_hash)) 114 { 115 struct MHD_Response *resp; 116 117 resp = MHD_response_from_empty (MHD_HTTP_STATUS_NOT_MODIFIED); 118 TALER_MHD2_add_global_headers (resp, 119 false); 120 return MHD_action_from_response (request, 121 resp); 122 } 123 } 124 } 125 /* We have a result, should fetch and return it! */ 126 break; 127 } 128 return MHD_action_from_response (request, 129 SH_make_backup (account, 130 MHD_HTTP_STATUS_OK)); 131 } 132 133 134 /** 135 * Return the current backup of @a account on @a request 136 * using @a default_http_status on success. 137 * 138 * @param account account to query 139 * @param default_http_status HTTP status to queue response 140 * with on success (#MHD_HTTP_STATUS_OK or #MHD_HTTP_STATUS_CONFLICT) 141 * @return MHD response 142 */ 143 struct MHD_Response * 144 SH_make_backup (const struct SYNC_AccountPublicKeyP *account, 145 enum MHD_HTTP_StatusCode default_http_status) 146 { 147 enum SYNC_DB_QueryStatus qs; 148 struct MHD_Response *resp; 149 struct SYNC_AccountSignatureP account_sig; 150 struct GNUNET_HashCode backup_hash; 151 struct GNUNET_HashCode prev_hash; 152 size_t backup_size; 153 void *backup; 154 155 qs = SYNCDB_lookup_backup_TR ( 156 account, 157 &account_sig, 158 &prev_hash, 159 &backup_hash, 160 &backup_size, 161 &backup); 162 switch (qs) 163 { 164 case SYNC_DB_OLD_BACKUP_MISSING: 165 GNUNET_break (0); 166 return TALER_MHD2_make_error (TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, 167 "unexpected return status (backup missing)"); 168 case SYNC_DB_OLD_BACKUP_MISMATCH: 169 GNUNET_break (0); 170 return TALER_MHD2_make_error (TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, 171 "unexpected return status (backup mismatch)"); 172 case SYNC_DB_PAYMENT_REQUIRED: 173 GNUNET_break (0); 174 return TALER_MHD2_make_error (TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, 175 "unexpected return status (payment required)") 176 ; 177 case SYNC_DB_HARD_ERROR: 178 GNUNET_break (0); 179 return TALER_MHD2_make_error (TALER_EC_GENERIC_DB_FETCH_FAILED, 180 NULL); 181 case SYNC_DB_SOFT_ERROR: 182 GNUNET_break (0); 183 return TALER_MHD2_make_error (TALER_EC_GENERIC_DB_SOFT_FAILURE, 184 NULL); 185 case SYNC_DB_NO_RESULTS: 186 GNUNET_break (0); 187 /* Note: can theoretically happen due to non-transactional nature if 188 the backup expired / was gc'ed JUST between the two SQL calls. 189 But too rare to handle properly, as doing a transaction would be 190 expensive. Just admit to failure ;-) */ 191 return TALER_MHD2_make_error (TALER_EC_GENERIC_DB_INVARIANT_FAILURE, 192 NULL); 193 case SYNC_DB_ONE_RESULT: 194 /* interesting case below */ 195 break; 196 } 197 resp = MHD_response_from_buffer (default_http_status, 198 backup_size, 199 backup, 200 &free, 201 backup); 202 TALER_MHD2_add_global_headers (resp, 203 false); 204 { 205 char *sig_s; 206 char *prev_s; 207 char *etag; 208 char *etagq; 209 210 sig_s = GNUNET_STRINGS_data_to_string_alloc (&account_sig, 211 sizeof (account_sig)); 212 prev_s = GNUNET_STRINGS_data_to_string_alloc (&prev_hash, 213 sizeof (prev_hash)); 214 etag = GNUNET_STRINGS_data_to_string_alloc (&backup_hash, 215 sizeof (backup_hash)); 216 GNUNET_break (MHD_SC_OK == 217 MHD_response_add_header (resp, 218 "Sync-Signature", 219 sig_s)); 220 GNUNET_break (MHD_SC_OK == 221 MHD_response_add_header (resp, 222 "Sync-Previous", 223 prev_s)); 224 GNUNET_asprintf (&etagq, 225 "\"%s\"", 226 etag); 227 GNUNET_break (MHD_SC_OK == 228 MHD_response_add_header (resp, 229 MHD_HTTP_HEADER_ETAG, 230 etagq)); 231 GNUNET_free (etagq); 232 GNUNET_free (etag); 233 GNUNET_free (prev_s); 234 GNUNET_free (sig_s); 235 } 236 return resp; 237 }