LCOV - code coverage report
Current view: top level - lib - exchange_api_post-management-wire.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 73.8 % 145 107
Test Date: 2026-04-14 15:39:31 Functions: 100.0 % 5 5

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 2015-2026 Taler Systems SA
       4              : 
       5              :   TALER is free software; you can redistribute it and/or modify it under the
       6              :   terms of the GNU 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 General Public License for more details.
      12              : 
      13              :   You should have received a copy of the GNU General Public License along with
      14              :   TALER; see the file COPYING.  If not, see
      15              :   <http://www.gnu.org/licenses/>
      16              : */
      17              : /**
      18              :  * @file lib/exchange_api_post-management-wire.c
      19              :  * @brief functions to enable an exchange wire method / bank account
      20              :  * @author Christian Grothoff
      21              :  */
      22              : #include "taler/taler_json_lib.h"
      23              : #include <gnunet/gnunet_curl_lib.h>
      24              : #include <microhttpd.h>
      25              : #include "taler/exchange/post-management-wire.h"
      26              : #include "exchange_api_curl_defaults.h"
      27              : #include "taler/taler_signatures.h"
      28              : #include "taler/taler_curl_lib.h"
      29              : 
      30              : 
      31              : struct TALER_EXCHANGE_PostManagementWireHandle
      32              : {
      33              : 
      34              :   /**
      35              :    * The base URL for this request.
      36              :    */
      37              :   char *base_url;
      38              : 
      39              :   /**
      40              :    * The full URL for this request, set during _start.
      41              :    */
      42              :   char *url;
      43              : 
      44              :   /**
      45              :    * Minor context that holds body and headers.
      46              :    */
      47              :   struct TALER_CURL_PostContext post_ctx;
      48              : 
      49              :   /**
      50              :    * Handle for the request.
      51              :    */
      52              :   struct GNUNET_CURL_Job *job;
      53              : 
      54              :   /**
      55              :    * Function to call with the result.
      56              :    */
      57              :   TALER_EXCHANGE_PostManagementWireCallback cb;
      58              : 
      59              :   /**
      60              :    * Closure for @a cb.
      61              :    */
      62              :   TALER_EXCHANGE_POST_MANAGEMENT_WIRE_RESULT_CLOSURE *cb_cls;
      63              : 
      64              :   /**
      65              :    * Reference to the execution context.
      66              :    */
      67              :   struct GNUNET_CURL_Context *ctx;
      68              : 
      69              :   /**
      70              :    * Payto URI of the exchange's bank account.
      71              :    */
      72              :   char *payto_uri_str;
      73              : 
      74              :   /**
      75              :    * URL of the conversion service, or NULL.
      76              :    */
      77              :   char *conversion_url;
      78              : 
      79              :   /**
      80              :    * URL of the open banking gateway, or NULL.
      81              :    */
      82              :   char *open_banking_gateway;
      83              : 
      84              :   /**
      85              :    * URL of the wire transfer gateway, or NULL.
      86              :    */
      87              :   char *wire_transfer_gateway;
      88              : 
      89              :   /**
      90              :    * JSON encoding of debit restrictions (we hold a reference).
      91              :    */
      92              :   json_t *debit_restrictions;
      93              : 
      94              :   /**
      95              :    * JSON encoding of credit restrictions (we hold a reference).
      96              :    */
      97              :   json_t *credit_restrictions;
      98              : 
      99              :   /**
     100              :    * When was this decided?
     101              :    */
     102              :   struct GNUNET_TIME_Timestamp validity_start;
     103              : 
     104              :   /**
     105              :    * Signature affirming the wire addition.
     106              :    */
     107              :   struct TALER_MasterSignatureP master_sig1;
     108              : 
     109              :   /**
     110              :    * Signature affirming the validity of the account for clients.
     111              :    */
     112              :   struct TALER_MasterSignatureP master_sig2;
     113              : 
     114              :   /**
     115              :    * Label to use when showing the account to users (or NULL).
     116              :    */
     117              :   char *bank_label;
     118              : 
     119              :   /**
     120              :    * Priority for ordering the bank accounts.
     121              :    */
     122              :   int64_t priority;
     123              : 
     124              : };
     125              : 
     126              : 
     127              : /**
     128              :  * Function called when we're done processing the
     129              :  * HTTP POST /management/wire request.
     130              :  *
     131              :  * @param cls the `struct TALER_EXCHANGE_PostManagementWireHandle`
     132              :  * @param response_code HTTP response code, 0 on error
     133              :  * @param response response body, NULL if not in JSON
     134              :  */
     135              : static void
     136           25 : handle_wire_finished (void *cls,
     137              :                       long response_code,
     138              :                       const void *response)
     139              : {
     140           25 :   struct TALER_EXCHANGE_PostManagementWireHandle *pmwh = cls;
     141           25 :   const json_t *json = response;
     142           25 :   struct TALER_EXCHANGE_PostManagementWireResponse res = {
     143           25 :     .hr.http_status = (unsigned int) response_code,
     144              :     .hr.reply = json
     145              :   };
     146              : 
     147           25 :   pmwh->job = NULL;
     148           25 :   switch (response_code)
     149              :   {
     150            0 :   case 0:
     151              :     /* no reply */
     152            0 :     res.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     153            0 :     res.hr.hint = "server offline?";
     154            0 :     break;
     155           23 :   case MHD_HTTP_NO_CONTENT:
     156           23 :     break;
     157            0 :   case MHD_HTTP_BAD_REQUEST:
     158            0 :     res.hr.ec = TALER_JSON_get_error_code (json);
     159            0 :     res.hr.hint = TALER_JSON_get_error_hint (json);
     160            0 :     break;
     161            2 :   case MHD_HTTP_FORBIDDEN:
     162            2 :     res.hr.ec = TALER_JSON_get_error_code (json);
     163            2 :     res.hr.hint = TALER_JSON_get_error_hint (json);
     164            2 :     break;
     165            0 :   case MHD_HTTP_CONFLICT:
     166            0 :     res.hr.ec = TALER_JSON_get_error_code (json);
     167            0 :     res.hr.hint = TALER_JSON_get_error_hint (json);
     168            0 :     break;
     169            0 :   default:
     170              :     /* unexpected response code */
     171            0 :     GNUNET_break_op (0);
     172            0 :     res.hr.ec = TALER_JSON_get_error_code (json);
     173            0 :     res.hr.hint = TALER_JSON_get_error_hint (json);
     174            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     175              :                 "Unexpected response code %u/%d for exchange management enable wire\n",
     176              :                 (unsigned int) response_code,
     177              :                 (int) res.hr.ec);
     178            0 :     break;
     179              :   }
     180           25 :   if (NULL != pmwh->cb)
     181              :   {
     182           25 :     pmwh->cb (pmwh->cb_cls,
     183              :               &res);
     184           25 :     pmwh->cb = NULL;
     185              :   }
     186           25 :   TALER_EXCHANGE_post_management_wire_cancel (pmwh);
     187           25 : }
     188              : 
     189              : 
     190              : struct TALER_EXCHANGE_PostManagementWireHandle *
     191           25 : TALER_EXCHANGE_post_management_wire_create (
     192              :   struct GNUNET_CURL_Context *ctx,
     193              :   const char *url,
     194              :   const struct TALER_FullPayto payto_uri,
     195              :   struct GNUNET_TIME_Timestamp validity_start,
     196              :   const struct TALER_MasterSignatureP *master_sig1,
     197              :   const struct TALER_MasterSignatureP *master_sig2)
     198              : {
     199              :   struct TALER_EXCHANGE_PostManagementWireHandle *pmwh;
     200              :   char *msg;
     201              : 
     202           25 :   msg = TALER_payto_validate (payto_uri);
     203           25 :   if (NULL != msg)
     204              :   {
     205            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     206              :                 "payto URI is malformed: %s\n",
     207              :                 msg);
     208            0 :     GNUNET_free (msg);
     209            0 :     return NULL;
     210              :   }
     211           25 :   pmwh = GNUNET_new (struct TALER_EXCHANGE_PostManagementWireHandle);
     212           25 :   pmwh->ctx = ctx;
     213           25 :   pmwh->base_url = GNUNET_strdup (url);
     214           25 :   pmwh->payto_uri_str = GNUNET_strdup (payto_uri.full_payto);
     215           25 :   pmwh->debit_restrictions = json_array ();
     216           25 :   GNUNET_assert (NULL != pmwh->debit_restrictions);
     217           25 :   pmwh->credit_restrictions = json_array ();
     218           25 :   GNUNET_assert (NULL != pmwh->credit_restrictions);
     219           25 :   pmwh->validity_start = validity_start;
     220           25 :   pmwh->master_sig1 = *master_sig1;
     221           25 :   pmwh->master_sig2 = *master_sig2;
     222           25 :   return pmwh;
     223              : }
     224              : 
     225              : 
     226              : enum GNUNET_GenericReturnValue
     227           25 : TALER_EXCHANGE_post_management_wire_set_options_ (
     228              :   struct TALER_EXCHANGE_PostManagementWireHandle *pmwh,
     229              :   unsigned int num_options,
     230              :   const struct TALER_EXCHANGE_PostManagementWireOptionValue options[])
     231              : {
     232          168 :   for (unsigned int i = 0; i < num_options; i++)
     233              :   {
     234          168 :     const struct TALER_EXCHANGE_PostManagementWireOptionValue *opt
     235          168 :       = &options[i];
     236              : 
     237          168 :     switch (opt->option)
     238              :     {
     239           25 :     case TALER_EXCHANGE_POST_MANAGEMENT_WIRE_OPTION_END:
     240           25 :       return GNUNET_OK;
     241           25 :     case TALER_EXCHANGE_POST_MANAGEMENT_WIRE_OPTION_BANK_LABEL:
     242           25 :       GNUNET_free (pmwh->bank_label);
     243           50 :       pmwh->bank_label = (NULL != opt->details.bank_label)
     244            8 :         ? GNUNET_strdup (opt->details.bank_label)
     245           25 :         : NULL;
     246           25 :       break;
     247           17 :     case TALER_EXCHANGE_POST_MANAGEMENT_WIRE_OPTION_PRIORITY:
     248           17 :       pmwh->priority = opt->details.priority;
     249           17 :       break;
     250           17 :     case TALER_EXCHANGE_POST_MANAGEMENT_WIRE_OPTION_CONVERSION_URL:
     251           17 :       GNUNET_free (pmwh->conversion_url);
     252           34 :       pmwh->conversion_url = (NULL != opt->details.conversion_url)
     253            0 :         ? GNUNET_strdup (opt->details.conversion_url)
     254           17 :         : NULL;
     255           17 :       break;
     256           17 :     case TALER_EXCHANGE_POST_MANAGEMENT_WIRE_OPTION_OPEN_BANKING_GATEWAY:
     257           17 :       GNUNET_free (pmwh->open_banking_gateway);
     258           34 :       pmwh->open_banking_gateway = (NULL != opt->details.open_banking_gateway)
     259            0 :         ? GNUNET_strdup (opt->details.open_banking_gateway)
     260           17 :         : NULL;
     261           17 :       break;
     262           17 :     case TALER_EXCHANGE_POST_MANAGEMENT_WIRE_OPTION_WIRE_TRANSFER_GATEWAY:
     263           17 :       GNUNET_free (pmwh->wire_transfer_gateway);
     264           34 :       pmwh->wire_transfer_gateway = (NULL != opt->details.wire_transfer_gateway)
     265            0 :         ? GNUNET_strdup (opt->details.wire_transfer_gateway)
     266           17 :         : NULL;
     267           17 :       break;
     268           25 :     case TALER_EXCHANGE_POST_MANAGEMENT_WIRE_OPTION_CREDIT_RESTRICTIONS:
     269           25 :       json_decref (pmwh->credit_restrictions);
     270           50 :       pmwh->credit_restrictions = (NULL != opt->details.credit_restrictions)
     271           25 :         ? json_incref ((json_t *) opt->details.credit_restrictions)
     272           25 :         : json_array ();
     273           25 :       break;
     274           25 :     case TALER_EXCHANGE_POST_MANAGEMENT_WIRE_OPTION_DEBIT_RESTRICTIONS:
     275           25 :       json_decref (pmwh->debit_restrictions);
     276           50 :       pmwh->debit_restrictions = (NULL != opt->details.debit_restrictions)
     277           25 :         ? json_incref ((json_t *) opt->details.debit_restrictions)
     278           25 :         : json_array ();
     279           25 :       break;
     280            0 :     default:
     281            0 :       GNUNET_break (0);
     282            0 :       return GNUNET_SYSERR;
     283              :     }
     284              :   }
     285            0 :   return GNUNET_OK;
     286              : }
     287              : 
     288              : 
     289              : enum TALER_ErrorCode
     290           25 : TALER_EXCHANGE_post_management_wire_start (
     291              :   struct TALER_EXCHANGE_PostManagementWireHandle *pmwh,
     292              :   TALER_EXCHANGE_PostManagementWireCallback cb,
     293              :   TALER_EXCHANGE_POST_MANAGEMENT_WIRE_RESULT_CLOSURE *cb_cls)
     294              : {
     295              :   CURL *eh;
     296              :   json_t *body;
     297           25 :   struct TALER_FullPayto payto_uri = {
     298           25 :     .full_payto = pmwh->payto_uri_str
     299              :   };
     300              : 
     301           25 :   pmwh->cb = cb;
     302           25 :   pmwh->cb_cls = cb_cls;
     303           25 :   pmwh->url = TALER_url_join (pmwh->base_url,
     304              :                               "management/wire",
     305              :                               NULL);
     306           25 :   if (NULL == pmwh->url)
     307              :   {
     308            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     309              :                 "Could not construct request URL.\n");
     310            0 :     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
     311              :   }
     312           25 :   body = GNUNET_JSON_PACK (
     313              :     TALER_JSON_pack_full_payto ("payto_uri",
     314              :                                 payto_uri),
     315              :     GNUNET_JSON_pack_array_incref ("debit_restrictions",
     316              :                                    pmwh->debit_restrictions),
     317              :     GNUNET_JSON_pack_array_incref ("credit_restrictions",
     318              :                                    pmwh->credit_restrictions),
     319              :     GNUNET_JSON_pack_allow_null (
     320              :       GNUNET_JSON_pack_string ("conversion_url",
     321              :                                pmwh->conversion_url)),
     322              :     GNUNET_JSON_pack_allow_null (
     323              :       GNUNET_JSON_pack_string ("open_banking_gateway",
     324              :                                pmwh->open_banking_gateway)),
     325              :     GNUNET_JSON_pack_allow_null (
     326              :       GNUNET_JSON_pack_string ("wire_transfer_gateway",
     327              :                                pmwh->wire_transfer_gateway)),
     328              :     GNUNET_JSON_pack_allow_null (
     329              :       GNUNET_JSON_pack_string ("bank_label",
     330              :                                pmwh->bank_label)),
     331              :     GNUNET_JSON_pack_int64 ("priority",
     332              :                             pmwh->priority),
     333              :     GNUNET_JSON_pack_data_auto ("master_sig_add",
     334              :                                 &pmwh->master_sig1),
     335              :     GNUNET_JSON_pack_data_auto ("master_sig_wire",
     336              :                                 &pmwh->master_sig2),
     337              :     GNUNET_JSON_pack_timestamp ("validity_start",
     338              :                                 pmwh->validity_start));
     339           25 :   eh = TALER_EXCHANGE_curl_easy_get_ (pmwh->url);
     340           50 :   if ( (NULL == eh) ||
     341              :        (GNUNET_OK !=
     342           25 :         TALER_curl_easy_post (&pmwh->post_ctx,
     343              :                               eh,
     344              :                               body)) )
     345              :   {
     346            0 :     GNUNET_break (0);
     347            0 :     if (NULL != eh)
     348            0 :       curl_easy_cleanup (eh);
     349            0 :     json_decref (body);
     350            0 :     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
     351              :   }
     352           25 :   json_decref (body);
     353           25 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     354              :               "Requesting URL '%s'\n",
     355              :               pmwh->url);
     356           50 :   pmwh->job = GNUNET_CURL_job_add2 (pmwh->ctx,
     357              :                                     eh,
     358           25 :                                     pmwh->post_ctx.headers,
     359              :                                     &handle_wire_finished,
     360              :                                     pmwh);
     361           25 :   if (NULL == pmwh->job)
     362            0 :     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
     363           25 :   return TALER_EC_NONE;
     364              : }
     365              : 
     366              : 
     367              : void
     368           25 : TALER_EXCHANGE_post_management_wire_cancel (
     369              :   struct TALER_EXCHANGE_PostManagementWireHandle *pmwh)
     370              : {
     371           25 :   if (NULL != pmwh->job)
     372              :   {
     373            0 :     GNUNET_CURL_job_cancel (pmwh->job);
     374            0 :     pmwh->job = NULL;
     375              :   }
     376           25 :   TALER_curl_easy_post_finished (&pmwh->post_ctx);
     377           25 :   json_decref (pmwh->debit_restrictions);
     378           25 :   json_decref (pmwh->credit_restrictions);
     379           25 :   GNUNET_free (pmwh->payto_uri_str);
     380           25 :   GNUNET_free (pmwh->conversion_url);
     381           25 :   GNUNET_free (pmwh->open_banking_gateway);
     382           25 :   GNUNET_free (pmwh->wire_transfer_gateway);
     383           25 :   GNUNET_free (pmwh->bank_label);
     384           25 :   GNUNET_free (pmwh->url);
     385           25 :   GNUNET_free (pmwh->base_url);
     386           25 :   GNUNET_free (pmwh);
     387           25 : }
     388              : 
     389              : 
     390              : /* end of exchange_api_post-management-wire.c */
        

Generated by: LCOV version 2.0-1