anastasis

Credential backup and recovery protocol and service
Log | Files | Refs | Submodules | README | LICENSE

anastasis_authorization_plugin_iban.c (24945B)


      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 
    123 
    124 /**
    125  * Returned by #get_message() when the configured messages file has no entry
    126  * for the requested ID.  This is an installation error, but it must not be
    127  * allowed to reach GNUNET_asprintf() as a NULL format string.  The text
    128  * deliberately contains no printf conversions, so substituting it for any
    129  * template is safe whatever argument list the caller passes.
    130  */
    131 #define MISSING_MESSAGE \
    132         "The provider is misconfigured: a message template is missing."
    133 
    134 
    135 /**
    136  * Obtain internationalized message @a msg_id from @a ctx using
    137  * language preferences of @a conn.
    138  *
    139  * @param messages JSON object to lookup message from
    140  * @param conn connection to lookup message for
    141  * @param msg_id unique message ID
    142  * @return the requested message, or #MISSING_MESSAGE if it was not
    143  *         configured; never NULL
    144  */
    145 static const char *
    146 get_message (const json_t *messages,
    147              struct MHD_Connection *conn,
    148              const char *msg_id)
    149 {
    150   const char *accept_lang;
    151 
    152   accept_lang = MHD_lookup_connection_value (conn,
    153                                              MHD_HEADER_KIND,
    154                                              MHD_HTTP_HEADER_ACCEPT_LANGUAGE);
    155   if (NULL == accept_lang)
    156     accept_lang = "en_US";
    157   {
    158     const char *ret;
    159     struct GNUNET_JSON_Specification spec[] = {
    160       TALER_JSON_spec_i18n_string (msg_id,
    161                                    accept_lang,
    162                                    &ret),
    163       GNUNET_JSON_spec_end ()
    164     };
    165 
    166     if (GNUNET_OK !=
    167         GNUNET_JSON_parse (messages,
    168                            spec,
    169                            NULL, NULL))
    170     {
    171       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    172                   "Message `%s' is missing from the configured messages file\n",
    173                   msg_id);
    174       GNUNET_JSON_parse_free (spec);
    175       return MISSING_MESSAGE;
    176     }
    177     GNUNET_JSON_parse_free (spec);
    178     if (NULL == ret)
    179     {
    180       /* The parser of TALER_JSON_spec_i18n_string returns #GNUNET_OK even
    181          when the field is absent or is not a string, in which case it stores
    182          NULL; the check above therefore never fires for a missing message and
    183          this one is what keeps NULL out of the format argument of the
    184          GNUNET_asprintf() calls below. */
    185       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    186                   "Message `%s' is missing or not a string in the configured messages file\n",
    187                   msg_id);
    188       return MISSING_MESSAGE;
    189     }
    190     return ret;
    191   }
    192 }
    193 
    194 
    195 /**
    196  * Validate @a data is a well-formed input into the challenge method,
    197  * i.e. @a data is a well-formed iban number for sending an IBAN, or
    198  * a well-formed e-mail address for sending an e-mail. Not expected to
    199  * check that the iban number or e-mail account actually exists.
    200  *
    201  * To be possibly used before issuing a 402 payment required to the client.
    202  *
    203  * @param cls closure with a `struct IBAN_Context`
    204  * @param connection HTTP client request (for queuing response)
    205  * @param truth_mime mime type of @e data
    206  * @param data input to validate (i.e. is it a valid iban number, etc.)
    207  * @param data_length number of bytes in @a data
    208  * @return #GNUNET_OK if @a data is valid,
    209  *         #GNUNET_NO if @a data is invalid and a reply was successfully queued on @a connection
    210  *         #GNUNET_SYSERR if @a data invalid but we failed to queue a reply on @a connection
    211  */
    212 static enum GNUNET_GenericReturnValue
    213 iban_validate (void *cls,
    214                struct MHD_Connection *connection,
    215                const char *truth_mime,
    216                const char *data,
    217                size_t data_length)
    218 {
    219   char *iban_number;
    220   char *emsg;
    221 
    222   iban_number = GNUNET_strndup (data,
    223                                 data_length);
    224   emsg = TALER_iban_validate (iban_number);
    225   if (NULL != emsg)
    226   {
    227     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    228                 "Invalid IBAN `%s' provided: %s\n",
    229                 iban_number,
    230                 emsg);
    231     GNUNET_free (iban_number);
    232     if (MHD_NO ==
    233         TALER_MHD_reply_with_error (connection,
    234                                     MHD_HTTP_CONFLICT,
    235                                     TALER_EC_ANASTASIS_IBAN_INVALID,
    236                                     emsg))
    237     {
    238       GNUNET_free (emsg);
    239       return GNUNET_SYSERR;
    240     }
    241     GNUNET_free (emsg);
    242     return GNUNET_NO;
    243   }
    244   GNUNET_free (iban_number);
    245   return GNUNET_OK;
    246 }
    247 
    248 
    249 /**
    250  * Begin issuing authentication challenge to user based on @a data.
    251  * Sends IBAN.
    252  *
    253  * @param cls closure with a `struct IBAN_Context`
    254  * @param trigger function to call when we made progress
    255  * @param trigger_cls closure for @a trigger
    256  * @param truth_uuid Identifier of the challenge, to be (if possible) included in the
    257  *             interaction with the user
    258  * @param code secret code that the user has to provide back to satisfy the challenge in
    259  *             the main anastasis protocol
    260  * @param data input to validate (i.e. is it a valid iban number, etc.)
    261  * @param data_length number of bytes in @a data
    262  * @return state to track progress on the authorization operation, NULL on failure
    263  */
    264 static struct ANASTASIS_AUTHORIZATION_State *
    265 iban_start (void *cls,
    266             GNUNET_SCHEDULER_TaskCallback trigger,
    267             void *trigger_cls,
    268             const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid,
    269             uint64_t code,
    270             const void *data,
    271             size_t data_length)
    272 {
    273   struct IBAN_Context *ctx = cls;
    274   struct ANASTASIS_AUTHORIZATION_State *as;
    275 
    276   as = GNUNET_new (struct ANASTASIS_AUTHORIZATION_State);
    277   as->trigger = trigger;
    278   as->trigger_cls = trigger_cls;
    279   as->ctx = ctx;
    280   as->truth_uuid = *truth_uuid;
    281   as->code = code;
    282   as->iban_number = GNUNET_strndup (data,
    283                                     data_length);
    284   return as;
    285 }
    286 
    287 
    288 /**
    289  * Function called when we received a wire transfer
    290  * with the respective code from the specified IBAN.
    291  *
    292  * @param cls our `struct ANASTASIS_AUHTORIZATION_State`
    293  * @param extra string describing amount transferred
    294  * @param extra_size number of byes in @a extra
    295  */
    296 static void
    297 bank_event_cb (void *cls,
    298                const void *extra,
    299                size_t extra_size)
    300 {
    301   struct ANASTASIS_AUTHORIZATION_State *as = cls;
    302   char *amount_s;
    303 
    304   if (NULL != extra)
    305   {
    306     struct TALER_Amount amount;
    307 
    308     /* The amount is only carried for diagnostics: the authoritative value is
    309        re-read from the database by #check_payment_ok(), so we must not keep
    310        (or act on) whatever the notification claims. */
    311     amount_s = GNUNET_strndup (extra,
    312                                extra_size);
    313     if (GNUNET_OK !=
    314         TALER_string_to_amount (amount_s,
    315                                 &amount))
    316     {
    317       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    318                   "Expected amount in event notification, got `%s'\n",
    319                   amount_s);
    320     }
    321     GNUNET_free (amount_s);
    322   }
    323   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    324               "IBAN event triggers resumption of request handling\n");
    325   MHD_resume_connection (as->connection);
    326   as->trigger (as->trigger_cls);
    327 }
    328 
    329 
    330 #include "iban.c"
    331 
    332 
    333 /**
    334  * Check if the @a wire_subject matches the challenge in the context
    335  * and if the @a amount is sufficient. If so, return true.
    336  *
    337  * @param cls a `const struct ANASTASIS_AUTHORIZATION_State *`
    338  * @param amount the amount that was transferred
    339  * @param wire_subject a wire subject we received
    340  * @return true if the wire transfer satisfied the check
    341  */
    342 static bool
    343 check_payment_ok (void *cls,
    344                   const struct TALER_Amount *amount,
    345                   const char *wire_subject)
    346 {
    347   const struct ANASTASIS_AUTHORIZATION_State *as = cls;
    348   struct IBAN_Context *ctx = as->ctx;
    349   uint64_t code;
    350 
    351   if (GNUNET_OK !=
    352       extract_code (wire_subject,
    353                     &code))
    354     return false;
    355   /* The database stamps every row with the currency configured for this
    356      provider, so a mismatch here means the plugin was configured for a
    357      different currency than the rest of the service.  Refusing is the only
    358      safe answer: comparing the magnitudes across currencies would let a
    359      transfer of "5 XYZ" authorize a challenge that costs 5 EUR. */
    360   if (GNUNET_YES !=
    361       TALER_amount_cmp_currency (amount,
    362                                  &ctx->expected_amount))
    363   {
    364     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    365                 "Received `%s', but [authorization-iban]/COST is denominated in `%s'\n",
    366                 amount->currency,
    367                 ctx->expected_amount.currency);
    368     return false;
    369   }
    370   if (1 ==
    371       TALER_amount_cmp (&ctx->expected_amount,
    372                         amount))
    373   {
    374     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    375                 "Amount `%s' insufficient for authorization\n",
    376                 TALER_amount2s (amount));
    377     return false;
    378   }
    379   return (code == as->code);
    380 }
    381 
    382 
    383 /**
    384  * Check if we have received a wire transfer with a subject
    385  * authorizing the disclosure of the credential in the meantime.
    386  *
    387  * @param as state to check for
    388  * @return WTS_SUCCESS if a transfer was received,
    389  *         WTS_NOT_READY if no transfer was received,
    390  *         WTS_FAILED_WITH_REPLY if we had an internal error and queued a reply
    391  *         WTS_FAILED_WITHOUT_REPLY if we had an internal error and failed to queue a reply
    392  */
    393 static enum
    394 {
    395   WTS_SUCCESS,
    396   WTS_NOT_READY,
    397   WTS_FAILED_WITH_REPLY,
    398   WTS_FAILED_WITHOUT_REPLY
    399 }
    400 test_wire_transfers (struct ANASTASIS_AUTHORIZATION_State *as)
    401 {
    402   enum GNUNET_DB_QueryStatus qs;
    403   struct GNUNET_TIME_Absolute now;
    404   struct GNUNET_TIME_Timestamp limit;
    405 
    406   now = GNUNET_TIME_absolute_get ();
    407   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    408               "Testing for wire transfers\n");
    409   limit = GNUNET_TIME_absolute_to_timestamp (
    410     GNUNET_TIME_absolute_subtract (now,
    411                                    CODE_VALIDITY_PERIOD));
    412   qs = ANASTASIS_DB_iterate_auth_iban_transfers (
    413     as->iban_number,
    414     limit,
    415     &check_payment_ok,
    416     as);
    417   switch (qs)
    418   {
    419   case GNUNET_DB_STATUS_HARD_ERROR:
    420   case GNUNET_DB_STATUS_SOFT_ERROR:
    421     return (MHD_YES ==
    422             TALER_MHD_reply_with_error (as->connection,
    423                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
    424                                         TALER_EC_GENERIC_DB_FETCH_FAILED,
    425                                         NULL))
    426            ? WTS_FAILED_WITH_REPLY
    427            : WTS_FAILED_WITHOUT_REPLY;
    428   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    429     return WTS_NOT_READY;
    430   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    431     break;
    432   }
    433   qs = ANASTASIS_DB_update_to_challenge_code_satisfied (
    434     &as->truth_uuid,
    435     as->code);
    436   GNUNET_break (qs > 0);
    437   return WTS_SUCCESS;
    438 }
    439 
    440 
    441 /**
    442  * Respond with instructions to the user how to
    443  * satisfy the challenge.
    444  *
    445  * @param as authorization state
    446  * @param connection HTTP client request (for queuing response, such as redirection to video portal)
    447  * @return state of the request
    448  */
    449 static enum ANASTASIS_AUTHORIZATION_ChallengeResult
    450 iban_challenge (struct ANASTASIS_AUTHORIZATION_State *as,
    451                 struct MHD_Connection *connection)
    452 {
    453   struct IBAN_Context *ctx = as->ctx;
    454   const char *mime;
    455   const char *lang;
    456   enum MHD_Result mres;
    457 
    458   mime = MHD_lookup_connection_value (connection,
    459                                       MHD_HEADER_KIND,
    460                                       MHD_HTTP_HEADER_ACCEPT);
    461   if (NULL == mime)
    462     mime = "text/plain";
    463   lang = MHD_lookup_connection_value (connection,
    464                                       MHD_HEADER_KIND,
    465                                       MHD_HTTP_HEADER_ACCEPT_LANGUAGE);
    466   if (NULL == lang)
    467     lang = "en";
    468 
    469   /* Build HTTP response */
    470   {
    471     struct MHD_Response *resp;
    472 
    473     if (0.0 < TALER_pattern_matches (mime,
    474                                      "application/json"))
    475     {
    476       char subject[64];
    477 
    478       GNUNET_snprintf (subject,
    479                        sizeof (subject),
    480                        "Anastasis %llu",
    481                        (unsigned long long) as->code);
    482       resp = TALER_MHD_MAKE_JSON_PACK (
    483         GNUNET_JSON_pack_string ("challenge_type",
    484                                  "IBAN_WIRE"),
    485         GNUNET_JSON_pack_object_steal (
    486           "wire_details",
    487           GNUNET_JSON_PACK (
    488             GNUNET_JSON_pack_uint64 (
    489               "answer_code",
    490               as->code),
    491             TALER_JSON_pack_amount (
    492               "challenge_amount",
    493               &ctx->expected_amount),
    494             GNUNET_JSON_pack_string (
    495               "credit_iban",
    496               ctx->business_iban),
    497             GNUNET_JSON_pack_string (
    498               "business_name",
    499               ctx->business_name),
    500             GNUNET_JSON_pack_string (
    501               "wire_transfer_subject",
    502               subject))));
    503     }
    504     else
    505     {
    506       size_t reply_len;
    507       char *reply;
    508 
    509       reply_len = GNUNET_asprintf (&reply,
    510                                    get_message (ctx->messages,
    511                                                 connection,
    512                                                 "instructions"),
    513                                    TALER_amount2s (&ctx->expected_amount),
    514                                    ctx->business_name,
    515                                    ctx->business_iban,
    516                                    (unsigned long long) as->code);
    517       resp = MHD_create_response_from_buffer (reply_len,
    518                                               reply,
    519                                               MHD_RESPMEM_MUST_COPY);
    520       GNUNET_free (reply);
    521       TALER_MHD_add_global_headers (resp,
    522                                     false);
    523       GNUNET_break (MHD_YES ==
    524                     MHD_add_response_header (resp,
    525                                              MHD_HTTP_HEADER_CONTENT_TYPE,
    526                                              "text/plain"));
    527     }
    528     mres = MHD_queue_response (connection,
    529                                MHD_HTTP_OK,
    530                                resp);
    531     MHD_destroy_response (resp);
    532     if (MHD_YES != mres)
    533       return ANASTASIS_AUTHORIZATION_CRES_SUCCESS_REPLY_FAILED;
    534     return ANASTASIS_AUTHORIZATION_CRES_SUCCESS;
    535   }
    536 }
    537 
    538 
    539 /**
    540  * Begin issuing authentication challenge to user based on @a data.
    541  * I.e. start to send IBAN or e-mail or launch video identification.
    542  *
    543  * @param as authorization state
    544  * @param timeout how long do we have to produce a reply
    545  * @param challenge_response hash of the challenge response, or NULL
    546  * @param connection HTTP client request (for queuing response, such as redirection to video portal)
    547  * @return state of the request
    548  */
    549 static enum ANASTASIS_AUTHORIZATION_SolveResult
    550 iban_solve (struct ANASTASIS_AUTHORIZATION_State *as,
    551             struct GNUNET_TIME_Absolute timeout,
    552             const struct GNUNET_HashCode *challenge_response,
    553             struct MHD_Connection *connection)
    554 {
    555   enum MHD_Result mres;
    556   enum GNUNET_DB_QueryStatus qs;
    557   struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
    558   struct GNUNET_TIME_Timestamp after;
    559 
    560   if (NULL == as->eh)
    561   {
    562     struct IbanEventP espec = {
    563       .header.size = htons (sizeof (espec)),
    564       .header.type = htons (TALER_DBEVENT_ANASTASIS_AUTH_IBAN_TRANSFER),
    565       .code = GNUNET_htonll (as->code)
    566     };
    567 
    568     GNUNET_CRYPTO_hash (as->iban_number,
    569                         strlen (as->iban_number),
    570                         &espec.debit_iban_hash);
    571     as->eh = ANASTASIS_DB_event_listen (
    572       &espec.header,
    573       GNUNET_TIME_absolute_get_remaining (
    574         timeout),
    575       &bank_event_cb,
    576       as);
    577   }
    578   after = GNUNET_TIME_absolute_to_timestamp (
    579     GNUNET_TIME_absolute_subtract (now,
    580                                    CODE_VALIDITY_PERIOD));
    581   qs = ANASTASIS_DB_get_exists_challenge_code_satisfied (
    582     &as->truth_uuid,
    583     as->code,
    584     after);
    585   switch (qs)
    586   {
    587   case GNUNET_DB_STATUS_HARD_ERROR:
    588   case GNUNET_DB_STATUS_SOFT_ERROR:
    589     mres = TALER_MHD_reply_with_error (connection,
    590                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
    591                                        TALER_EC_GENERIC_DB_FETCH_FAILED,
    592                                        "get exists challenge code satisfied");
    593     if (MHD_YES != mres)
    594       return ANASTASIS_AUTHORIZATION_SRES_FAILED_REPLY_FAILED;
    595     return ANASTASIS_AUTHORIZATION_SRES_FAILED;
    596   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    597     switch (test_wire_transfers (as))
    598     {
    599     case WTS_SUCCESS:
    600       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    601                   "IBAN authorization finished!\n");
    602       return ANASTASIS_AUTHORIZATION_SRES_FINISHED;
    603     case WTS_NOT_READY:
    604       break;   /* continue below */
    605     case WTS_FAILED_WITH_REPLY:
    606       return ANASTASIS_AUTHORIZATION_SRES_FAILED;
    607     case WTS_FAILED_WITHOUT_REPLY:
    608       return ANASTASIS_AUTHORIZATION_SRES_FAILED_REPLY_FAILED;
    609     }
    610     if (GNUNET_TIME_absolute_is_future (timeout))
    611     {
    612       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    613                   "Suspending IBAN check until %s\n",
    614                   GNUNET_TIME_absolute2s (timeout));
    615       as->connection = connection;
    616       MHD_suspend_connection (connection);
    617       return ANASTASIS_AUTHORIZATION_SRES_SUSPENDED;
    618     }
    619     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    620                 "Timeout reached at %s, failing request\n",
    621                 GNUNET_TIME_absolute2s (timeout));
    622     mres = TALER_MHD_reply_with_error (connection,
    623                                        MHD_HTTP_FORBIDDEN,
    624                                        TALER_EC_ANASTASIS_IBAN_MISSING_TRANSFER,
    625                                        NULL);
    626     if (MHD_YES != mres)
    627       return ANASTASIS_AUTHORIZATION_SRES_FAILED_REPLY_FAILED;
    628     return ANASTASIS_AUTHORIZATION_SRES_FAILED;
    629   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    630     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    631                 "IBAN authorization finished!\n");
    632     return ANASTASIS_AUTHORIZATION_SRES_FINISHED;
    633   }
    634   /* should be impossible */
    635   GNUNET_break (0);
    636   return ANASTASIS_AUTHORIZATION_SRES_FAILED_REPLY_FAILED;
    637 }
    638 
    639 
    640 /**
    641  * Free internal state associated with @a as.
    642  *
    643  * @param as state to clean up
    644  */
    645 static void
    646 iban_cleanup (struct ANASTASIS_AUTHORIZATION_State *as)
    647 {
    648   if (NULL != as->eh)
    649   {
    650     ANASTASIS_DB_event_listen_cancel (as->eh);
    651     as->eh = NULL;
    652   }
    653   GNUNET_free (as->iban_number);
    654   GNUNET_free (as);
    655 }
    656 
    657 
    658 /**
    659  * Initialize email based authorization plugin
    660  *
    661  * @param cls a `struct ANASTASIS_AuthorizationContext`
    662  * @return NULL on error, otherwise a `struct ANASTASIS_AuthorizationPlugin`
    663  */
    664 void *
    665 libanastasis_plugin_authorization_iban_init (void *cls);
    666 
    667 /* declaration to fix compiler warning */
    668 void *
    669 libanastasis_plugin_authorization_iban_init (void *cls)
    670 {
    671   struct ANASTASIS_AuthorizationContext *ac = cls;
    672   struct ANASTASIS_AuthorizationPlugin *plugin;
    673   const struct GNUNET_CONFIGURATION_Handle *cfg = ac->cfg;
    674   struct IBAN_Context *ctx;
    675 
    676   ctx = GNUNET_new (struct IBAN_Context);
    677   if (GNUNET_OK !=
    678       GNUNET_CONFIGURATION_get_value_string (cfg,
    679                                              "authorization-iban",
    680                                              "CREDIT_IBAN",
    681                                              &ctx->business_iban))
    682   {
    683     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    684                                "authorization-iban",
    685                                "CREDIT_IBAN");
    686     GNUNET_free (ctx);
    687     return NULL;
    688   }
    689   if (GNUNET_OK !=
    690       TALER_config_get_amount (cfg,
    691                                "authorization-iban",
    692                                "COST",
    693                                &ctx->expected_amount))
    694   {
    695     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    696                                "authorization-iban",
    697                                "COST");
    698     GNUNET_free (ctx);
    699     return NULL;
    700   }
    701   if (GNUNET_OK !=
    702       GNUNET_CONFIGURATION_get_value_string (cfg,
    703                                              "authorization-iban",
    704                                              "BUSINESS_NAME",
    705                                              &ctx->business_name))
    706   {
    707     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    708                                "authorization-iban",
    709                                "BUSINESS_NAME");
    710     GNUNET_free (ctx->business_iban);
    711     GNUNET_free (ctx);
    712     return NULL;
    713   }
    714   {
    715     char *fn;
    716     json_error_t err;
    717     char *tmp;
    718 
    719     tmp = GNUNET_OS_installation_get_path (ANASTASIS_project_data (),
    720                                            GNUNET_OS_IPK_DATADIR);
    721     GNUNET_asprintf (&fn,
    722                      "%sauthorization-iban-messages.json",
    723                      tmp);
    724     GNUNET_free (tmp);
    725     ctx->messages = json_load_file (fn,
    726                                     JSON_REJECT_DUPLICATES,
    727                                     &err);
    728     if (NULL == ctx->messages)
    729     {
    730       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    731                   "Failed to load messages from `%s': %s at %d:%d\n",
    732                   fn,
    733                   err.text,
    734                   err.line,
    735                   err.column);
    736       GNUNET_free (ctx->business_iban);
    737       GNUNET_free (ctx->business_name);
    738       GNUNET_free (fn);
    739       GNUNET_free (ctx);
    740       return NULL;
    741     }
    742     GNUNET_free (fn);
    743   }
    744   ctx->ac = ac;
    745   plugin = GNUNET_new (struct ANASTASIS_AuthorizationPlugin);
    746   plugin->payment_plugin_managed = true;
    747   plugin->retry_counter = UINT32_MAX; /* long polling */
    748   plugin->code_validity_period = CODE_VALIDITY_PERIOD;
    749   plugin->code_rotation_period = GNUNET_TIME_UNIT_ZERO;
    750   plugin->code_retransmission_frequency = GNUNET_TIME_UNIT_ZERO; /* not applicable */
    751   plugin->cls = ctx;
    752   plugin->validate = &iban_validate;
    753   plugin->start = &iban_start;
    754   plugin->challenge = &iban_challenge;
    755   plugin->solve = &iban_solve;
    756   plugin->cleanup = &iban_cleanup;
    757   return plugin;
    758 }
    759 
    760 
    761 /**
    762  * Unload authorization plugin
    763  *
    764  * @param cls a `struct ANASTASIS_AuthorizationPlugin`
    765  * @return NULL (always)
    766  */
    767 void *
    768 libanastasis_plugin_authorization_iban_done (void *cls);
    769 
    770 /* declaration to fix compiler warning */
    771 void *
    772 libanastasis_plugin_authorization_iban_done (void *cls)
    773 {
    774   struct ANASTASIS_AuthorizationPlugin *plugin = cls;
    775   struct IBAN_Context *ctx = plugin->cls;
    776 
    777   json_decref (ctx->messages);
    778   GNUNET_free (ctx->business_iban);
    779   GNUNET_free (ctx->business_name);
    780   GNUNET_free (ctx);
    781   GNUNET_free (plugin);
    782   return NULL;
    783 }