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 }