testing_api_cmd_backup_upload.c (12644B)
1 /* 2 This file is part of SYNC 3 Copyright (C) 2014-2023 Taler Systems SA 4 5 SYNC is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as 7 published by the Free Software Foundation; either version 3, or 8 (at your option) any later version. 9 10 SYNC 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 General Public License for more details. 14 15 You should have received a copy of the GNU General Public 16 License along with SYNC; see the file COPYING. If not, see 17 <http://www.gnu.org/licenses/> 18 */ 19 /** 20 * @file testing/testing_api_cmd_backup_upload.c 21 * @brief command to upload data to the sync backend service. 22 * @author Christian Grothoff 23 */ 24 #include "platform.h" 25 #include "sync_service.h" 26 #include "sync_testing_lib.h" 27 #include <taler/taler_util.h> 28 #include <taler/taler_testing_lib.h> 29 #include <taler/taler_merchant_service.h> 30 #include "sync_testing_lib.h" 31 32 /** 33 * State for a "backup upload" CMD. 34 */ 35 struct BackupUploadState 36 { 37 38 /** 39 * Eddsa private key. 40 */ 41 struct SYNC_AccountPrivateKeyP sync_priv; 42 43 /** 44 * Eddsa public key. 45 */ 46 struct SYNC_AccountPublicKeyP sync_pub; 47 48 /** 49 * Hash of the previous upload (maybe bogus if 50 * #SYNC_TESTING_UO_PREV_HASH_WRONG is set in @e uo). 51 * Maybe all zeros if there was no previous upload. 52 */ 53 struct GNUNET_HashCode prev_hash; 54 55 /** 56 * Hash of the current upload. 57 */ 58 struct GNUNET_HashCode curr_hash; 59 60 /** 61 * The /backups POST operation handle. 62 */ 63 struct SYNC_UploadOperation *uo; 64 65 /** 66 * URL of the sync backend. 67 */ 68 const char *sync_url; 69 70 /** 71 * Previous upload, or NULL for none. Used to calculate what THIS 72 * upload is based on. 73 */ 74 const char *prev_upload; 75 76 /** 77 * Last upload, or NULL for none, usually same as @e prev_upload. 78 * Used to check the response on #MHD_HTTP_CONFLICT. 79 */ 80 const char *last_upload; 81 82 /** 83 * Payment order ID we got back, if any. Otherwise NULL. 84 */ 85 const char *payment_order_id; 86 87 /** 88 * Claim token we got back, if any. Otherwise all zeros. 89 */ 90 struct TALER_ClaimTokenP token; 91 92 /** 93 * Payment order ID we are to provide in the request, may be NULL. 94 */ 95 const char *payment_order_req; 96 97 /** 98 * The interpreter state. 99 */ 100 struct TALER_TESTING_Interpreter *is; 101 102 /** 103 * The backup data we are uploading. 104 */ 105 const void *backup; 106 107 /** 108 * Number of bytes in @e backup. 109 */ 110 size_t backup_size; 111 112 /** 113 * Expected status code. 114 */ 115 unsigned int http_status; 116 117 /** 118 * Options for how we are supposed to do the upload. 119 */ 120 enum SYNC_TESTING_UploadOption uopt; 121 122 }; 123 124 125 /** 126 * Function called with the results of a #SYNC_upload(). 127 * 128 * @param cls closure 129 * @param ud details about the upload operation 130 */ 131 static void 132 backup_upload_cb (void *cls, 133 const struct SYNC_UploadDetails *ud) 134 { 135 struct BackupUploadState *bus = cls; 136 137 bus->uo = NULL; 138 if (ud->http_status != bus->http_status) 139 { 140 TALER_TESTING_unexpected_status (bus->is, 141 ud->http_status, 142 bus->http_status); 143 return; 144 } 145 switch (ud->us) 146 { 147 case SYNC_US_SUCCESS: 148 if (0 != GNUNET_memcmp ( 149 &bus->curr_hash, 150 ud->details.success.curr_backup_hash)) 151 { 152 GNUNET_break (0); 153 TALER_TESTING_interpreter_fail (bus->is); 154 return; 155 } 156 break; 157 case SYNC_US_PAYMENT_REQUIRED: 158 { 159 struct TALER_MERCHANT_PayUriData pd; 160 161 if (GNUNET_OK != 162 TALER_MERCHANT_parse_pay_uri ( 163 ud->details.payment_required.payment_request, 164 &pd)) 165 { 166 GNUNET_break (0); 167 TALER_TESTING_interpreter_fail (bus->is); 168 return; 169 } 170 bus->payment_order_id = GNUNET_strdup (pd.order_id); 171 if (NULL != pd.claim_token) 172 bus->token = *pd.claim_token; 173 TALER_MERCHANT_parse_pay_uri_free (&pd); 174 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 175 "Order ID from Sync service is `%s'\n", 176 bus->payment_order_id); 177 memset (&bus->curr_hash, 178 0, 179 sizeof (struct GNUNET_HashCode)); 180 } 181 break; 182 case SYNC_US_CONFLICTING_BACKUP: 183 { 184 const struct TALER_TESTING_Command *ref; 185 const struct GNUNET_HashCode *h; 186 187 ref = TALER_TESTING_interpreter_lookup_command 188 (bus->is, 189 bus->last_upload); 190 GNUNET_assert (NULL != ref); 191 GNUNET_assert (GNUNET_OK == 192 TALER_TESTING_get_trait_curr_hash (ref, 193 &h)); 194 if (0 != GNUNET_memcmp (h, 195 &ud->details.recovered_backup. 196 existing_backup_hash)) 197 { 198 GNUNET_break (0); 199 TALER_TESTING_interpreter_fail (bus->is); 200 return; 201 } 202 } 203 case SYNC_US_HTTP_ERROR: 204 break; 205 case SYNC_US_CLIENT_ERROR: 206 GNUNET_break (0); 207 TALER_TESTING_interpreter_fail (bus->is); 208 return; 209 case SYNC_US_SERVER_ERROR: 210 GNUNET_break (0); 211 TALER_TESTING_interpreter_fail (bus->is); 212 return; 213 } 214 TALER_TESTING_interpreter_next (bus->is); 215 } 216 217 218 /** 219 * Run a "backup upload" CMD. 220 * 221 * @param cls closure. 222 * @param cmd command currently being run. 223 * @param is interpreter state. 224 */ 225 static void 226 backup_upload_run (void *cls, 227 const struct TALER_TESTING_Command *cmd, 228 struct TALER_TESTING_Interpreter *is) 229 { 230 struct BackupUploadState *bus = cls; 231 232 bus->is = is; 233 if (NULL != bus->prev_upload) 234 { 235 const struct TALER_TESTING_Command *ref; 236 237 ref = TALER_TESTING_interpreter_lookup_command ( 238 is, 239 bus->prev_upload); 240 if (NULL == ref) 241 { 242 GNUNET_break (0); 243 TALER_TESTING_interpreter_fail (bus->is); 244 return; 245 } 246 { 247 const struct GNUNET_HashCode *h; 248 249 if (GNUNET_OK == 250 TALER_TESTING_get_trait_curr_hash (ref, 251 &h)) 252 { 253 bus->prev_hash = *h; 254 } 255 } 256 { 257 const struct SYNC_AccountPrivateKeyP *priv; 258 259 if (GNUNET_OK != 260 TALER_TESTING_get_trait_sync_account_priv (ref, 261 &priv)) 262 { 263 GNUNET_break (0); 264 TALER_TESTING_interpreter_fail (bus->is); 265 return; 266 } 267 bus->sync_priv = *priv; 268 } 269 { 270 const struct SYNC_AccountPublicKeyP *pub; 271 272 if (GNUNET_OK != 273 TALER_TESTING_get_trait_sync_account_pub (ref, 274 &pub)) 275 { 276 GNUNET_break (0); 277 TALER_TESTING_interpreter_fail (bus->is); 278 return; 279 } 280 bus->sync_pub = *pub; 281 } 282 if (0 != (SYNC_TESTING_UO_REFERENCE_ORDER_ID & bus->uopt)) 283 { 284 const char *order_id; 285 286 if (GNUNET_OK != 287 TALER_TESTING_get_trait_order_id (ref, 288 &order_id)) 289 { 290 GNUNET_break (0); 291 TALER_TESTING_interpreter_fail (bus->is); 292 return; 293 } 294 bus->payment_order_req = order_id; 295 if (NULL == bus->payment_order_req) 296 { 297 GNUNET_break (0); 298 TALER_TESTING_interpreter_fail (bus->is); 299 return; 300 } 301 } 302 } 303 else 304 { 305 GNUNET_CRYPTO_eddsa_key_create (&bus->sync_priv.eddsa_priv); 306 GNUNET_CRYPTO_eddsa_key_get_public (&bus->sync_priv.eddsa_priv, 307 &bus->sync_pub.eddsa_pub); 308 } 309 if (0 != (SYNC_TESTING_UO_PREV_HASH_WRONG & bus->uopt)) 310 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, 311 &bus->prev_hash, 312 sizeof (struct GNUNET_HashCode)); 313 GNUNET_CRYPTO_hash (bus->backup, 314 bus->backup_size, 315 &bus->curr_hash); 316 bus->uo = SYNC_upload (TALER_TESTING_interpreter_get_context (is), 317 bus->sync_url, 318 &bus->sync_priv, 319 ( ( (NULL != bus->prev_upload) && 320 (GNUNET_NO == GNUNET_is_zero ( 321 &bus->prev_hash)) ) || 322 (0 != (SYNC_TESTING_UO_PREV_HASH_WRONG 323 & bus->uopt)) ) 324 ? &bus->prev_hash 325 : NULL, 326 bus->backup_size, 327 bus->backup, 328 (0 != (SYNC_TESTING_UO_REQUEST_PAYMENT & bus->uopt)) 329 ? SYNC_PO_FORCE_PAYMENT 330 : SYNC_PO_NONE, 331 bus->payment_order_req, 332 &backup_upload_cb, 333 bus); 334 if (NULL == bus->uo) 335 { 336 GNUNET_break (0); 337 TALER_TESTING_interpreter_fail (bus->is); 338 return; 339 } 340 } 341 342 343 /** 344 * Free the state of a "backup upload" CMD, and possibly 345 * cancel it if it did not complete. 346 * 347 * @param cls closure. 348 * @param cmd command being freed. 349 */ 350 static void 351 backup_upload_cleanup (void *cls, 352 const struct TALER_TESTING_Command *cmd) 353 { 354 struct BackupUploadState *bus = cls; 355 356 if (NULL != bus->uo) 357 { 358 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 359 "Command '%s' did not complete (backup upload)\n", 360 cmd->label); 361 SYNC_upload_cancel (bus->uo); 362 bus->uo = NULL; 363 } 364 GNUNET_free (bus); 365 } 366 367 368 /** 369 * Offer internal data to other commands. 370 * 371 * @param cls closure 372 * @param[out] ret result (could be anything) 373 * @param trait name of the trait 374 * @param index index number of the object to extract. 375 * @return #GNUNET_OK on success 376 */ 377 static enum GNUNET_GenericReturnValue 378 backup_upload_traits (void *cls, 379 const void **ret, 380 const char *trait, 381 unsigned int index) 382 { 383 struct BackupUploadState *bus = cls; 384 struct TALER_TESTING_Trait straits[] = { 385 TALER_TESTING_make_trait_curr_hash (&bus->curr_hash), 386 TALER_TESTING_make_trait_prev_hash (&bus->prev_hash), 387 TALER_TESTING_make_trait_claim_token (&bus->token), 388 TALER_TESTING_make_trait_sync_account_pub (&bus->sync_pub), 389 TALER_TESTING_make_trait_sync_account_priv (&bus->sync_priv), 390 TALER_TESTING_make_trait_order_id (bus->payment_order_id), 391 TALER_TESTING_trait_end () 392 }; 393 struct TALER_TESTING_Trait ftraits[] = { 394 TALER_TESTING_make_trait_claim_token (&bus->token), 395 TALER_TESTING_make_trait_sync_account_pub (&bus->sync_pub), 396 TALER_TESTING_make_trait_sync_account_priv (&bus->sync_priv), 397 TALER_TESTING_make_trait_order_id (bus->payment_order_id), 398 TALER_TESTING_trait_end () 399 }; 400 401 402 return TALER_TESTING_get_trait ((NULL != bus->payment_order_req) 403 ? ftraits 404 : straits, 405 ret, 406 trait, 407 index); 408 } 409 410 411 /** 412 * Make the "backup upload" command. 413 * 414 * @param label command label 415 * @param sync_url base URL of the sync serving 416 * the policy store request. 417 * @param prev_upload reference to a previous upload we are 418 * supposed to update, NULL for none 419 * @param last_upload reference to the last upload for the 420 * same account, used to check result on MHD_HTTP_CONFLICT 421 * @param uo upload options 422 * @param http_status expected HTTP status. 423 * @param backup_data data to upload 424 * @param backup_data_size number of bytes in @a backup_data 425 * @return the command 426 */ 427 struct TALER_TESTING_Command 428 SYNC_TESTING_cmd_backup_upload (const char *label, 429 const char *sync_url, 430 const char *prev_upload, 431 const char *last_upload, 432 enum SYNC_TESTING_UploadOption uo, 433 unsigned int http_status, 434 const void *backup_data, 435 size_t backup_data_size) 436 { 437 struct BackupUploadState *bus; 438 439 bus = GNUNET_new (struct BackupUploadState); 440 bus->http_status = http_status; 441 bus->prev_upload = prev_upload; 442 bus->last_upload = last_upload; 443 bus->uopt = uo; 444 bus->sync_url = sync_url; 445 bus->backup = backup_data; 446 bus->backup_size = backup_data_size; 447 { 448 struct TALER_TESTING_Command cmd = { 449 .cls = bus, 450 .label = label, 451 .run = &backup_upload_run, 452 .cleanup = &backup_upload_cleanup, 453 .traits = &backup_upload_traits 454 }; 455 456 return cmd; 457 } 458 }