anastasis_authorization_plugin_iban.c (23113B)
1 /* 2 This file is part of Anastasis 3 Copyright (C) 2021 Anastasis SARL 4 5 Anastasis 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 Anastasis 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 Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file anastasis_authorization_plugin_iban.c 18 * @brief authorization plugin wire transfer based 19 * @author Christian Grothoff 20 */ 21 #include "platform.h" 22 #include "anastasis_authorization_plugin.h" 23 #include <taler/taler_mhd_lib.h> 24 #include <taler/taler_json_lib.h> 25 #include <gnunet/gnunet_db_lib.h> 26 #include "anastasis_authorization_lib.h" 27 #include "anastasis_database_lib.h" 28 #include "anastasis_util_lib.h" 29 #include "iban.h" 30 31 /** 32 * How long is a code valid once generated? Very long 33 * here as we do not want to refuse authentication 34 * just because the user took a while to execute the 35 * wire transfer (and then get back to their recovery 36 * operation). 37 */ 38 #define CODE_VALIDITY_PERIOD GNUNET_TIME_UNIT_MONTHS 39 40 41 /** 42 * Saves the State of a authorization plugin. 43 */ 44 struct IBAN_Context 45 { 46 47 /** 48 * Messages of the plugin, read from a resource file. 49 */ 50 json_t *messages; 51 52 /** 53 * IBAN of our business, must be credited in the SEPA 54 * wire transfer. 55 */ 56 char *business_iban; 57 58 /** 59 * Name of our business, for the SEPA wire transfer. 60 */ 61 char *business_name; 62 63 /** 64 * Handle to interact with a authorization backend. 65 */ 66 const struct ANASTASIS_AuthorizationContext *ac; 67 68 /** 69 * Amount we expect to be transferred. 70 */ 71 struct TALER_Amount expected_amount; 72 73 }; 74 75 76 /** 77 * Saves the State of a authorization process 78 */ 79 struct ANASTASIS_AUTHORIZATION_State 80 { 81 /** 82 * Public key of the challenge which is authorised 83 */ 84 struct ANASTASIS_CRYPTO_TruthUUIDP truth_uuid; 85 86 /** 87 * Code which is sent to the user (here sent via IBAN) 88 */ 89 uint64_t code; 90 91 /** 92 * Our plugin context. 93 */ 94 struct IBAN_Context *ctx; 95 96 /** 97 * Function to call when we made progress. 98 */ 99 GNUNET_SCHEDULER_TaskCallback trigger; 100 101 /** 102 * Closure for @e trigger. 103 */ 104 void *trigger_cls; 105 106 /** 107 * holds the truth information 108 */ 109 char *iban_number; 110 111 /** 112 * Our client connection, set if suspended. 113 */ 114 struct MHD_Connection *connection; 115 116 /** 117 * Handler for database event we are waiting for. 118 */ 119 struct GNUNET_DB_EventHandler *eh; 120 121 /** 122 * Amount that was transferred. 123 */ 124 struct TALER_Amount amount; 125 }; 126 127 128 /** 129 * Obtain internationalized message @a msg_id from @a ctx using 130 * language preferences of @a conn. 131 * 132 * @param messages JSON object to lookup message from 133 * @param conn connection to lookup message for 134 * @param msg_id unique message ID 135 * @return NULL if message was not found 136 */ 137 static const char * 138 get_message (const json_t *messages, 139 struct MHD_Connection *conn, 140 const char *msg_id) 141 { 142 const char *accept_lang; 143 144 accept_lang = MHD_lookup_connection_value (conn, 145 MHD_HEADER_KIND, 146 MHD_HTTP_HEADER_ACCEPT_LANGUAGE); 147 if (NULL == accept_lang) 148 accept_lang = "en_US"; 149 { 150 const char *ret; 151 struct GNUNET_JSON_Specification spec[] = { 152 TALER_JSON_spec_i18n_string (msg_id, 153 accept_lang, 154 &ret), 155 GNUNET_JSON_spec_end () 156 }; 157 158 if (GNUNET_OK != 159 GNUNET_JSON_parse (messages, 160 spec, 161 NULL, NULL)) 162 { 163 GNUNET_break (0); 164 GNUNET_JSON_parse_free (spec); 165 return NULL; 166 } 167 GNUNET_JSON_parse_free (spec); 168 return ret; 169 } 170 } 171 172 173 /** 174 * Validate @a data is a well-formed input into the challenge method, 175 * i.e. @a data is a well-formed iban number for sending an IBAN, or 176 * a well-formed e-mail address for sending an e-mail. Not expected to 177 * check that the iban number or e-mail account actually exists. 178 * 179 * To be possibly used before issuing a 402 payment required to the client. 180 * 181 * @param cls closure with a `struct IBAN_Context` 182 * @param connection HTTP client request (for queuing response) 183 * @param truth_mime mime type of @e data 184 * @param data input to validate (i.e. is it a valid iban number, etc.) 185 * @param data_length number of bytes in @a data 186 * @return #GNUNET_OK if @a data is valid, 187 * #GNUNET_NO if @a data is invalid and a reply was successfully queued on @a connection 188 * #GNUNET_SYSERR if @a data invalid but we failed to queue a reply on @a connection 189 */ 190 static enum GNUNET_GenericReturnValue 191 iban_validate (void *cls, 192 struct MHD_Connection *connection, 193 const char *truth_mime, 194 const char *data, 195 size_t data_length) 196 { 197 char *iban_number; 198 char *emsg; 199 200 iban_number = GNUNET_strndup (data, 201 data_length); 202 emsg = TALER_iban_validate (iban_number); 203 if (NULL != emsg) 204 { 205 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 206 "Invalid IBAN `%s' provided: %s\n", 207 iban_number, 208 emsg); 209 GNUNET_free (iban_number); 210 if (MHD_NO == 211 TALER_MHD_reply_with_error (connection, 212 MHD_HTTP_CONFLICT, 213 TALER_EC_ANASTASIS_IBAN_INVALID, 214 emsg)) 215 { 216 GNUNET_free (emsg); 217 return GNUNET_SYSERR; 218 } 219 GNUNET_free (emsg); 220 return GNUNET_NO; 221 } 222 GNUNET_free (iban_number); 223 return GNUNET_OK; 224 } 225 226 227 /** 228 * Begin issuing authentication challenge to user based on @a data. 229 * Sends IBAN. 230 * 231 * @param cls closure with a `struct IBAN_Context` 232 * @param trigger function to call when we made progress 233 * @param trigger_cls closure for @a trigger 234 * @param truth_uuid Identifier of the challenge, to be (if possible) included in the 235 * interaction with the user 236 * @param code secret code that the user has to provide back to satisfy the challenge in 237 * the main anastasis protocol 238 * @param data input to validate (i.e. is it a valid iban number, etc.) 239 * @param data_length number of bytes in @a data 240 * @return state to track progress on the authorization operation, NULL on failure 241 */ 242 static struct ANASTASIS_AUTHORIZATION_State * 243 iban_start (void *cls, 244 GNUNET_SCHEDULER_TaskCallback trigger, 245 void *trigger_cls, 246 const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, 247 uint64_t code, 248 const void *data, 249 size_t data_length) 250 { 251 struct IBAN_Context *ctx = cls; 252 struct ANASTASIS_AUTHORIZATION_State *as; 253 254 as = GNUNET_new (struct ANASTASIS_AUTHORIZATION_State); 255 as->trigger = trigger; 256 as->trigger_cls = trigger_cls; 257 as->ctx = ctx; 258 as->truth_uuid = *truth_uuid; 259 as->code = code; 260 as->iban_number = GNUNET_strndup (data, 261 data_length); 262 return as; 263 } 264 265 266 /** 267 * Function called when we received a wire transfer 268 * with the respective code from the specified IBAN. 269 * 270 * @param cls our `struct ANASTASIS_AUHTORIZATION_State` 271 * @param extra string describing amount transferred 272 * @param extra_size number of byes in @a extra 273 */ 274 static void 275 bank_event_cb (void *cls, 276 const void *extra, 277 size_t extra_size) 278 { 279 struct ANASTASIS_AUTHORIZATION_State *as = cls; 280 char *amount_s; 281 282 if (NULL != extra) 283 { 284 amount_s = GNUNET_strndup (extra, 285 extra_size); 286 if (GNUNET_OK != 287 TALER_string_to_amount (amount_s, 288 &as->amount)) 289 { 290 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 291 "Expected amount in event notification, got `%s'\n", 292 amount_s); 293 } 294 GNUNET_free (amount_s); 295 } 296 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 297 "IBAN event triggers resumption of request handling\n"); 298 MHD_resume_connection (as->connection); 299 as->trigger (as->trigger_cls); 300 } 301 302 303 #include "iban.c" 304 305 306 /** 307 * Check if the @a wire_subject matches the challenge in the context 308 * and if the @a amount is sufficient. If so, return true. 309 * 310 * @param cls a `const struct ANASTASIS_AUTHORIZATION_State *` 311 * @param amount the amount that was transferred 312 * @param wire_subject a wire subject we received 313 * @return true if the wire transfer satisfied the check 314 */ 315 static bool 316 check_payment_ok (void *cls, 317 const struct TALER_Amount *amount, 318 const char *wire_subject) 319 { 320 const struct ANASTASIS_AUTHORIZATION_State *as = cls; 321 struct IBAN_Context *ctx = as->ctx; 322 uint64_t code; 323 struct TALER_Amount camount; 324 325 if (GNUNET_OK != 326 extract_code (wire_subject, 327 &code)) 328 return false; 329 /* Database uses 'default' currency, but this 330 plugin may use a different currency (and the 331 same goes for the bank). So we fix this by 332 forcing the currency to be 'right'. */ 333 camount = *amount; 334 strcpy (camount.currency, 335 ctx->expected_amount.currency); 336 if (1 == 337 TALER_amount_cmp (&ctx->expected_amount, 338 &camount)) 339 { 340 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 341 "Amount `%s' insufficient for authorization\n", 342 TALER_amount2s (&camount)); 343 return false; 344 } 345 return (code == as->code); 346 } 347 348 349 /** 350 * Check if we have received a wire transfer with a subject 351 * authorizing the disclosure of the credential in the meantime. 352 * 353 * @param as state to check for 354 * @return WTS_SUCCESS if a transfer was received, 355 * WTS_NOT_READY if no transfer was received, 356 * WTS_FAILED_WITH_REPLY if we had an internal error and queued a reply 357 * WTS_FAILED_WITHOUT_REPLY if we had an internal error and failed to queue a reply 358 */ 359 static enum 360 { 361 WTS_SUCCESS, 362 WTS_NOT_READY, 363 WTS_FAILED_WITH_REPLY, 364 WTS_FAILED_WITHOUT_REPLY 365 } 366 test_wire_transfers (struct ANASTASIS_AUTHORIZATION_State *as) 367 { 368 enum GNUNET_DB_QueryStatus qs; 369 struct GNUNET_TIME_Absolute now; 370 struct GNUNET_TIME_Timestamp limit; 371 372 now = GNUNET_TIME_absolute_get (); 373 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 374 "Testing for wire transfers\n"); 375 limit = GNUNET_TIME_absolute_to_timestamp ( 376 GNUNET_TIME_absolute_subtract (now, 377 CODE_VALIDITY_PERIOD)); 378 qs = ANASTASIS_DB_test_auth_iban_payment ( 379 as->iban_number, 380 limit, 381 &check_payment_ok, 382 as); 383 switch (qs) 384 { 385 case GNUNET_DB_STATUS_HARD_ERROR: 386 case GNUNET_DB_STATUS_SOFT_ERROR: 387 return (MHD_YES == 388 TALER_MHD_reply_with_error (as->connection, 389 MHD_HTTP_INTERNAL_SERVER_ERROR, 390 TALER_EC_GENERIC_DB_FETCH_FAILED, 391 NULL)) 392 ? WTS_FAILED_WITH_REPLY 393 : WTS_FAILED_WITHOUT_REPLY; 394 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 395 return WTS_NOT_READY; 396 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 397 break; 398 } 399 qs = ANASTASIS_DB_mark_challenge_code_satisfied ( 400 &as->truth_uuid, 401 as->code); 402 GNUNET_break (qs > 0); 403 return WTS_SUCCESS; 404 } 405 406 407 /** 408 * Respond with instructions to the user how to 409 * satisfy the challenge. 410 * 411 * @param as authorization state 412 * @param connection HTTP client request (for queuing response, such as redirection to video portal) 413 * @return state of the request 414 */ 415 static enum ANASTASIS_AUTHORIZATION_ChallengeResult 416 iban_challenge (struct ANASTASIS_AUTHORIZATION_State *as, 417 struct MHD_Connection *connection) 418 { 419 struct IBAN_Context *ctx = as->ctx; 420 const char *mime; 421 const char *lang; 422 enum MHD_Result mres; 423 424 mime = MHD_lookup_connection_value (connection, 425 MHD_HEADER_KIND, 426 MHD_HTTP_HEADER_ACCEPT); 427 if (NULL == mime) 428 mime = "text/plain"; 429 lang = MHD_lookup_connection_value (connection, 430 MHD_HEADER_KIND, 431 MHD_HTTP_HEADER_ACCEPT_LANGUAGE); 432 if (NULL == lang) 433 lang = "en"; 434 435 /* Build HTTP response */ 436 { 437 struct MHD_Response *resp; 438 439 if (0.0 < TALER_pattern_matches (mime, 440 "application/json")) 441 { 442 char subject[64]; 443 444 GNUNET_snprintf (subject, 445 sizeof (subject), 446 "Anastasis %llu", 447 (unsigned long long) as->code); 448 resp = TALER_MHD_MAKE_JSON_PACK ( 449 GNUNET_JSON_pack_string ("challenge_type", 450 "IBAN_WIRE"), 451 GNUNET_JSON_pack_object_steal ( 452 "wire_details", 453 GNUNET_JSON_PACK ( 454 GNUNET_JSON_pack_uint64 ( 455 "answer_code", 456 as->code), 457 TALER_JSON_pack_amount ( 458 "challenge_amount", 459 &ctx->expected_amount), 460 GNUNET_JSON_pack_string ( 461 "credit_iban", 462 ctx->business_iban), 463 GNUNET_JSON_pack_string ( 464 "business_name", 465 ctx->business_name), 466 GNUNET_JSON_pack_string ( 467 "wire_transfer_subject", 468 subject)))); 469 } 470 else 471 { 472 size_t reply_len; 473 char *reply; 474 475 reply_len = GNUNET_asprintf (&reply, 476 get_message (ctx->messages, 477 connection, 478 "instructions"), 479 TALER_amount2s (&ctx->expected_amount), 480 ctx->business_name, 481 ctx->business_iban, 482 (unsigned long long) as->code); 483 resp = MHD_create_response_from_buffer (reply_len, 484 reply, 485 MHD_RESPMEM_MUST_COPY); 486 GNUNET_free (reply); 487 TALER_MHD_add_global_headers (resp, 488 false); 489 GNUNET_break (MHD_YES == 490 MHD_add_response_header (resp, 491 MHD_HTTP_HEADER_CONTENT_TYPE, 492 "text/plain")); 493 } 494 mres = MHD_queue_response (connection, 495 MHD_HTTP_OK, 496 resp); 497 MHD_destroy_response (resp); 498 if (MHD_YES != mres) 499 return ANASTASIS_AUTHORIZATION_CRES_SUCCESS_REPLY_FAILED; 500 return ANASTASIS_AUTHORIZATION_CRES_SUCCESS; 501 } 502 } 503 504 505 /** 506 * Begin issuing authentication challenge to user based on @a data. 507 * I.e. start to send IBAN or e-mail or launch video identification. 508 * 509 * @param as authorization state 510 * @param timeout how long do we have to produce a reply 511 * @param challenge_response hash of the challenge response, or NULL 512 * @param connection HTTP client request (for queuing response, such as redirection to video portal) 513 * @return state of the request 514 */ 515 static enum ANASTASIS_AUTHORIZATION_SolveResult 516 iban_solve (struct ANASTASIS_AUTHORIZATION_State *as, 517 struct GNUNET_TIME_Absolute timeout, 518 const struct GNUNET_HashCode *challenge_response, 519 struct MHD_Connection *connection) 520 { 521 enum MHD_Result mres; 522 enum GNUNET_DB_QueryStatus qs; 523 struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); 524 struct GNUNET_TIME_Timestamp after; 525 526 if (NULL == as->eh) 527 { 528 struct IbanEventP espec = { 529 .header.size = htons (sizeof (espec)), 530 .header.type = htons (TALER_DBEVENT_ANASTASIS_AUTH_IBAN_TRANSFER), 531 .code = GNUNET_htonll (as->code) 532 }; 533 534 GNUNET_CRYPTO_hash (as->iban_number, 535 strlen (as->iban_number), 536 &espec.debit_iban_hash); 537 as->eh = ANASTASIS_DB_event_listen ( 538 &espec.header, 539 GNUNET_TIME_absolute_get_remaining ( 540 timeout), 541 &bank_event_cb, 542 as); 543 } 544 after = GNUNET_TIME_absolute_to_timestamp ( 545 GNUNET_TIME_absolute_subtract (now, 546 CODE_VALIDITY_PERIOD)); 547 qs = ANASTASIS_DB_test_challenge_code_satisfied ( 548 &as->truth_uuid, 549 as->code, 550 after); 551 switch (qs) 552 { 553 case GNUNET_DB_STATUS_HARD_ERROR: 554 case GNUNET_DB_STATUS_SOFT_ERROR: 555 mres = TALER_MHD_reply_with_error (connection, 556 MHD_HTTP_INTERNAL_SERVER_ERROR, 557 TALER_EC_GENERIC_DB_FETCH_FAILED, 558 "test challenge code satisfied"); 559 if (MHD_YES != mres) 560 return ANASTASIS_AUTHORIZATION_SRES_FAILED_REPLY_FAILED; 561 return ANASTASIS_AUTHORIZATION_SRES_FAILED; 562 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 563 switch (test_wire_transfers (as)) 564 { 565 case WTS_SUCCESS: 566 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 567 "IBAN authorization finished!\n"); 568 return ANASTASIS_AUTHORIZATION_SRES_FINISHED; 569 case WTS_NOT_READY: 570 break; /* continue below */ 571 case WTS_FAILED_WITH_REPLY: 572 return ANASTASIS_AUTHORIZATION_SRES_FAILED; 573 case WTS_FAILED_WITHOUT_REPLY: 574 return ANASTASIS_AUTHORIZATION_SRES_FAILED_REPLY_FAILED; 575 } 576 if (GNUNET_TIME_absolute_is_future (timeout)) 577 { 578 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 579 "Suspending IBAN check until %s\n", 580 GNUNET_TIME_absolute2s (timeout)); 581 as->connection = connection; 582 MHD_suspend_connection (connection); 583 return ANASTASIS_AUTHORIZATION_SRES_SUSPENDED; 584 } 585 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 586 "Timeout reached at %s, failing request\n", 587 GNUNET_TIME_absolute2s (timeout)); 588 mres = TALER_MHD_reply_with_error (connection, 589 MHD_HTTP_FORBIDDEN, 590 TALER_EC_ANASTASIS_IBAN_MISSING_TRANSFER, 591 NULL); 592 if (MHD_YES != mres) 593 return ANASTASIS_AUTHORIZATION_SRES_FAILED_REPLY_FAILED; 594 return ANASTASIS_AUTHORIZATION_SRES_FAILED; 595 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 596 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 597 "IBAN authorization finished!\n"); 598 return ANASTASIS_AUTHORIZATION_SRES_FINISHED; 599 } 600 /* should be impossible */ 601 GNUNET_break (0); 602 return ANASTASIS_AUTHORIZATION_SRES_FAILED_REPLY_FAILED; 603 } 604 605 606 /** 607 * Free internal state associated with @a as. 608 * 609 * @param as state to clean up 610 */ 611 static void 612 iban_cleanup (struct ANASTASIS_AUTHORIZATION_State *as) 613 { 614 if (NULL != as->eh) 615 { 616 ANASTASIS_DB_event_listen_cancel (as->eh); 617 as->eh = NULL; 618 } 619 GNUNET_free (as->iban_number); 620 GNUNET_free (as); 621 } 622 623 624 /** 625 * Initialize email based authorization plugin 626 * 627 * @param cls a `struct ANASTASIS_AuthorizationContext` 628 * @return NULL on error, otherwise a `struct ANASTASIS_AuthorizationPlugin` 629 */ 630 void * 631 libanastasis_plugin_authorization_iban_init (void *cls); 632 633 /* declaration to fix compiler warning */ 634 void * 635 libanastasis_plugin_authorization_iban_init (void *cls) 636 { 637 struct ANASTASIS_AuthorizationContext *ac = cls; 638 struct ANASTASIS_AuthorizationPlugin *plugin; 639 const struct GNUNET_CONFIGURATION_Handle *cfg = ac->cfg; 640 struct IBAN_Context *ctx; 641 642 ctx = GNUNET_new (struct IBAN_Context); 643 if (GNUNET_OK != 644 GNUNET_CONFIGURATION_get_value_string (cfg, 645 "authorization-iban", 646 "CREDIT_IBAN", 647 &ctx->business_iban)) 648 { 649 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 650 "authorization-iban", 651 "CREDIT_IBAN"); 652 GNUNET_free (ctx); 653 return NULL; 654 } 655 if (GNUNET_OK != 656 TALER_config_get_amount (cfg, 657 "authorization-iban", 658 "COST", 659 &ctx->expected_amount)) 660 { 661 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 662 "authorization-iban", 663 "COST"); 664 GNUNET_free (ctx); 665 return NULL; 666 } 667 if (GNUNET_OK != 668 GNUNET_CONFIGURATION_get_value_string (cfg, 669 "authorization-iban", 670 "BUSINESS_NAME", 671 &ctx->business_name)) 672 { 673 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 674 "authorization-iban", 675 "BUSINESS_NAME"); 676 GNUNET_free (ctx->business_iban); 677 GNUNET_free (ctx); 678 return NULL; 679 } 680 { 681 char *fn; 682 json_error_t err; 683 char *tmp; 684 685 tmp = GNUNET_OS_installation_get_path (ANASTASIS_project_data (), 686 GNUNET_OS_IPK_DATADIR); 687 GNUNET_asprintf (&fn, 688 "%sauthorization-iban-messages.json", 689 tmp); 690 GNUNET_free (tmp); 691 ctx->messages = json_load_file (fn, 692 JSON_REJECT_DUPLICATES, 693 &err); 694 if (NULL == ctx->messages) 695 { 696 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 697 "Failed to load messages from `%s': %s at %d:%d\n", 698 fn, 699 err.text, 700 err.line, 701 err.column); 702 GNUNET_free (ctx->business_iban); 703 GNUNET_free (ctx->business_name); 704 GNUNET_free (fn); 705 GNUNET_free (ctx); 706 return NULL; 707 } 708 GNUNET_free (fn); 709 } 710 ctx->ac = ac; 711 plugin = GNUNET_new (struct ANASTASIS_AuthorizationPlugin); 712 plugin->payment_plugin_managed = true; 713 plugin->retry_counter = UINT32_MAX; /* long polling */ 714 plugin->code_validity_period = CODE_VALIDITY_PERIOD; 715 plugin->code_rotation_period = GNUNET_TIME_UNIT_ZERO; 716 plugin->code_retransmission_frequency = GNUNET_TIME_UNIT_ZERO; /* not applicable */ 717 plugin->cls = ctx; 718 plugin->validate = &iban_validate; 719 plugin->start = &iban_start; 720 plugin->challenge = &iban_challenge; 721 plugin->solve = &iban_solve; 722 plugin->cleanup = &iban_cleanup; 723 return plugin; 724 } 725 726 727 /** 728 * Unload authorization plugin 729 * 730 * @param cls a `struct ANASTASIS_AuthorizationPlugin` 731 * @return NULL (always) 732 */ 733 void * 734 libanastasis_plugin_authorization_iban_done (void *cls); 735 736 /* declaration to fix compiler warning */ 737 void * 738 libanastasis_plugin_authorization_iban_done (void *cls) 739 { 740 struct ANASTASIS_AuthorizationPlugin *plugin = cls; 741 struct IBAN_Context *ctx = plugin->cls; 742 743 json_decref (ctx->messages); 744 GNUNET_free (ctx->business_iban); 745 GNUNET_free (ctx->business_name); 746 GNUNET_free (ctx); 747 GNUNET_free (plugin); 748 return NULL; 749 }