LCOV - code coverage report
Current view: top level - bank-lib - bank_api_registration.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 0.0 % 149 0
Test Date: 2026-09-09 15:11:34 Functions: 0.0 % 4 0

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 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 bank-lib/bank_api_registration.c
      19              :  * @brief Implementation of the POST /registration request of the bank's HTTP API
      20              :  * @author Christian Grothoff
      21              :  */
      22              : #include "bank_api_common.h"
      23              : #include <microhttpd.h> /* just for HTTP status codes */
      24              : #include "taler/taler_json_lib.h"
      25              : #include "taler/taler_signatures.h"
      26              : #include "taler/taler_curl_lib.h"
      27              : 
      28              : 
      29              : /**
      30              :  * @brief A /registration Handle
      31              :  */
      32              : struct TALER_BANK_RegistrationHandle
      33              : {
      34              : 
      35              :   /**
      36              :    * The URL for this request.
      37              :    */
      38              :   char *request_url;
      39              : 
      40              :   /**
      41              :    * POST context.
      42              :    */
      43              :   struct TALER_CURL_PostContext post_ctx;
      44              : 
      45              :   /**
      46              :    * Handle for the request.
      47              :    */
      48              :   struct GNUNET_CURL_Job *job;
      49              : 
      50              :   /**
      51              :    * Function to call with the result.
      52              :    */
      53              :   TALER_BANK_RegistrationCallback cb;
      54              : 
      55              :   /**
      56              :    * Closure for @a cb.
      57              :    */
      58              :   void *cb_cls;
      59              : 
      60              : };
      61              : 
      62              : 
      63              : /**
      64              :  * Parse the "subject" field of a successful /registration response.
      65              :  * The field is a JSON object discriminated by "type".
      66              :  *
      67              :  * @param subject_json the JSON object to parse (the inner "subject" value)
      68              :  * @param[out] ts set to the parsed transfer subject on success
      69              :  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
      70              :  */
      71              : static enum GNUNET_GenericReturnValue
      72            0 : parse_transfer_subject (const json_t *subject_json,
      73              :                         struct TALER_BANK_TransferSubject *ts)
      74              : {
      75              :   const char *type_str;
      76              :   struct GNUNET_JSON_Specification type_spec[] = {
      77            0 :     GNUNET_JSON_spec_string ("type",
      78              :                              &type_str),
      79            0 :     GNUNET_JSON_spec_end ()
      80              :   };
      81              : 
      82            0 :   if (GNUNET_OK !=
      83            0 :       GNUNET_JSON_parse (subject_json,
      84              :                          type_spec,
      85              :                          NULL,
      86              :                          NULL))
      87              :   {
      88            0 :     GNUNET_break_op (0);
      89            0 :     return GNUNET_SYSERR;
      90              :   }
      91              : 
      92            0 :   if (0 == strcasecmp (type_str,
      93              :                        "SIMPLE"))
      94              :   {
      95              :     struct GNUNET_JSON_Specification spec[] = {
      96            0 :       TALER_JSON_spec_amount_any (
      97              :         "credit_amount",
      98              :         &ts->details.simple.credit_amount),
      99            0 :       GNUNET_JSON_spec_string (
     100              :         "subject",
     101            0 :         (const char **) &ts->details.simple.subject),
     102            0 :       GNUNET_JSON_spec_end ()
     103              :     };
     104              : 
     105            0 :     if (GNUNET_OK !=
     106            0 :         GNUNET_JSON_parse (subject_json,
     107              :                            spec,
     108              :                            NULL, NULL))
     109              :     {
     110            0 :       GNUNET_break_op (0);
     111            0 :       return GNUNET_SYSERR;
     112              :     }
     113            0 :     ts->format = TALER_BANK_SUBJECT_FORMAT_SIMPLE;
     114            0 :     return GNUNET_OK;
     115              :   }
     116            0 :   if (0 == strcasecmp (type_str,
     117              :                        "URI"))
     118              :   {
     119              :     struct GNUNET_JSON_Specification spec[] = {
     120            0 :       TALER_JSON_spec_amount_any (
     121              :         "credit_amount",
     122              :         &ts->details.uri.credit_amount),
     123            0 :       GNUNET_JSON_spec_string (
     124              :         "uri",
     125            0 :         (const char **) &ts->details.uri.uri),
     126            0 :       GNUNET_JSON_spec_end ()
     127              :     };
     128              : 
     129            0 :     if (GNUNET_OK !=
     130            0 :         GNUNET_JSON_parse (subject_json,
     131              :                            spec,
     132              :                            NULL, NULL))
     133              :     {
     134            0 :       GNUNET_break_op (0);
     135            0 :       return GNUNET_SYSERR;
     136              :     }
     137            0 :     ts->format = TALER_BANK_SUBJECT_FORMAT_URI;
     138            0 :     return GNUNET_OK;
     139              :   }
     140            0 :   if (0 == strcasecmp (type_str,
     141              :                        "CH_QR_BILL"))
     142              :   {
     143              :     struct GNUNET_JSON_Specification spec[] = {
     144            0 :       TALER_JSON_spec_amount_any (
     145              :         "credit_amount",
     146              :         &ts->details.ch_qr_bill.credit_amount),
     147            0 :       GNUNET_JSON_spec_string (
     148              :         "qr_reference_number",
     149            0 :         (const char **) &ts->details.ch_qr_bill.qr_reference_number),
     150            0 :       GNUNET_JSON_spec_end ()
     151              :     };
     152              : 
     153            0 :     if (GNUNET_OK !=
     154            0 :         GNUNET_JSON_parse (subject_json,
     155              :                            spec,
     156              :                            NULL, NULL))
     157              :     {
     158            0 :       GNUNET_break_op (0);
     159            0 :       return GNUNET_SYSERR;
     160              :     }
     161            0 :     ts->format = TALER_BANK_SUBJECT_FORMAT_CH_QR_BILL;
     162            0 :     return GNUNET_OK;
     163              :   }
     164            0 :   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     165              :               "Unknown transfer subject type `%s'\n",
     166              :               type_str);
     167            0 :   GNUNET_break_op (0);
     168            0 :   return GNUNET_SYSERR;
     169              : }
     170              : 
     171              : 
     172              : /**
     173              :  * Function called when we're done processing the HTTP POST /registration
     174              :  * request.
     175              :  *
     176              :  * @param cls the `struct TALER_BANK_RegistrationHandle`
     177              :  * @param response_code HTTP response code, 0 on error
     178              :  * @param response parsed JSON result, NULL on error
     179              :  */
     180              : static void
     181            0 : handle_registration_finished (void *cls,
     182              :                               long response_code,
     183              :                               const void *response)
     184              : {
     185            0 :   struct TALER_BANK_RegistrationHandle *rh = cls;
     186            0 :   const json_t *j = response;
     187            0 :   struct TALER_BANK_RegistrationResponse rr = {
     188              :     .http_status = response_code,
     189              :     .response = response
     190              :   };
     191              : 
     192            0 :   rh->job = NULL;
     193            0 :   switch (response_code)
     194              :   {
     195            0 :   case 0:
     196            0 :     rr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     197            0 :     break;
     198            0 :   case MHD_HTTP_OK:
     199              :     {
     200              :       const json_t *subjects;
     201              :       struct GNUNET_JSON_Specification spec[] = {
     202            0 :         GNUNET_JSON_spec_array_const ("subjects",
     203              :                                       &subjects),
     204            0 :         GNUNET_JSON_spec_timestamp ("expiration",
     205              :                                     &rr.details.ok.expiration),
     206            0 :         GNUNET_JSON_spec_end ()
     207              :       };
     208              : 
     209            0 :       if (GNUNET_OK !=
     210            0 :           GNUNET_JSON_parse (j,
     211              :                              spec,
     212              :                              NULL, NULL))
     213              :       {
     214            0 :         GNUNET_break_op (0);
     215            0 :         rr.http_status = 0;
     216            0 :         rr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     217            0 :         break;
     218              :       }
     219              : 
     220              :       {
     221            0 :         size_t n = json_array_size (subjects);
     222              :         struct TALER_BANK_TransferSubject *ts;
     223              :         size_t i;
     224              :         const json_t *subject;
     225              : 
     226            0 :         ts = GNUNET_new_array (GNUNET_NZL (n),
     227              :                                struct TALER_BANK_TransferSubject);
     228            0 :         json_array_foreach ((json_t *) subjects, i, subject)
     229              :         {
     230            0 :           if (GNUNET_OK !=
     231            0 :               parse_transfer_subject (subject,
     232            0 :                                       &ts[i]))
     233              :           {
     234            0 :             GNUNET_break_op (0);
     235            0 :             rr.http_status = 0;
     236            0 :             rr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     237            0 :             break;
     238              :           }
     239              :         }
     240            0 :         if (MHD_HTTP_OK == rr.http_status)
     241              :         {
     242            0 :           rr.details.ok.num_subjects = n;
     243            0 :           rr.details.ok.subjects = ts;
     244            0 :           rh->cb (rh->cb_cls,
     245              :                   &rr);
     246            0 :           GNUNET_free (ts);
     247            0 :           TALER_BANK_registration_cancel (rh);
     248            0 :           return;
     249              :         }
     250            0 :         GNUNET_free (ts);
     251              :       }
     252              :     }
     253            0 :     break;
     254            0 :   case MHD_HTTP_BAD_REQUEST:
     255              :     /* Either we or the service is buggy, or there is an API version conflict. */
     256            0 :     GNUNET_break_op (0);
     257            0 :     rr.ec = TALER_JSON_get_error_code (j);
     258            0 :     break;
     259            0 :   case MHD_HTTP_CONFLICT:
     260              :     /* Covers TALER_EC_BANK_DUPLICATE_RESERVE_PUB_SUBJECT,
     261              :        TALER_EC_BANK_UNSUPPORTED_SUBJECT_FORMAT,
     262              :        TALER_EC_BANK_DERIVATION_REUSE, and
     263              :        TALER_EC_BANK_BAD_SIGNATURE. */
     264            0 :     rr.ec = TALER_JSON_get_error_code (j);
     265            0 :     break;
     266            0 :   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     267              :     /* Server had an internal issue; we should retry, but this API
     268              :        leaves that to the application. */
     269            0 :     rr.ec = TALER_JSON_get_error_code (j);
     270            0 :     break;
     271            0 :   default:
     272              :     /* unexpected response code */
     273            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     274              :                 "Unexpected response code %u\n",
     275              :                 (unsigned int) response_code);
     276            0 :     GNUNET_break (0);
     277            0 :     rr.ec = TALER_JSON_get_error_code (j);
     278            0 :     break;
     279              :   }
     280            0 :   rh->cb (rh->cb_cls,
     281              :           &rr);
     282            0 :   TALER_BANK_registration_cancel (rh);
     283              : }
     284              : 
     285              : 
     286              : struct TALER_BANK_RegistrationHandle *
     287            0 : TALER_BANK_registration (
     288              :   struct GNUNET_CURL_Context *ctx,
     289              :   const char *base_url,
     290              :   const struct TALER_FullPayto *credit_account,
     291              :   const struct TALER_Amount *credit_amount,
     292              :   enum TALER_BankRegistrationType type,
     293              :   const union TALER_AccountPublicKeyP *account_pub,
     294              :   const struct TALER_PreparedTransferAuthorizationPrivateKeyP *
     295              :   authorization_priv,
     296              :   bool recurrent,
     297              :   TALER_BANK_RegistrationCallback res_cb,
     298              :   void *res_cb_cls)
     299              : {
     300              :   struct TALER_PreparedTransferAuthorizationPublicKeyP authorization_pub;
     301              :   struct TALER_PreparedTransferAuthorizationSignatureP authorization_sig;
     302              :   struct TALER_BANK_RegistrationHandle *rh;
     303              :   const char *type_str;
     304              :   json_t *reg_obj;
     305              :   CURL *eh;
     306              : 
     307            0 :   TALER_wallet_prepared_transfer_registration_sign (
     308              :     *credit_account,
     309              :     credit_amount,
     310              :     type,
     311              :     recurrent,
     312              :     account_pub,
     313              :     authorization_priv,
     314              :     &authorization_sig);
     315            0 :   GNUNET_CRYPTO_eddsa_key_get_public (&authorization_priv->eddsa_priv,
     316              :                                       &authorization_pub.eddsa_pub);
     317              : 
     318            0 :   switch (type)
     319              :   {
     320            0 :   case TALER_BANK_REGISTRATION_TYPE_RESERVE:
     321            0 :     type_str = "reserve";
     322            0 :     break;
     323            0 :   case TALER_BANK_REGISTRATION_TYPE_KYC:
     324            0 :     type_str = "kyc";
     325            0 :     break;
     326            0 :   default:
     327            0 :     GNUNET_break (0);
     328            0 :     return NULL;
     329              :   }
     330              : 
     331            0 :   reg_obj = GNUNET_JSON_PACK (
     332              :     TALER_JSON_pack_full_payto ("credit_account",
     333              :                                 *credit_account),
     334              :     TALER_JSON_pack_amount ("credit_amount",
     335              :                             credit_amount),
     336              :     GNUNET_JSON_pack_string ("type",
     337              :                              type_str),
     338              :     GNUNET_JSON_pack_string ("alg",
     339              :                              "EdDSA"),
     340              :     GNUNET_JSON_pack_data_auto ("account_pub",
     341              :                                 account_pub),
     342              :     GNUNET_JSON_pack_data_auto ("authorization_pub",
     343              :                                 &authorization_pub),
     344              :     GNUNET_JSON_pack_data_auto ("authorization_sig",
     345              :                                 &authorization_sig),
     346              :     GNUNET_JSON_pack_bool ("recurrent",
     347              :                            recurrent));
     348            0 :   rh = GNUNET_new (struct TALER_BANK_RegistrationHandle);
     349            0 :   rh->cb = res_cb;
     350            0 :   rh->cb_cls = res_cb_cls;
     351            0 :   rh->request_url = TALER_url_join (base_url,
     352              :                                     "registration",
     353              :                                     NULL);
     354            0 :   if (NULL == rh->request_url)
     355              :   {
     356            0 :     GNUNET_free (rh);
     357            0 :     json_decref (reg_obj);
     358            0 :     return NULL;
     359              :   }
     360            0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     361              :               "Requesting wire transfer subject registration at `%s'\n",
     362              :               rh->request_url);
     363            0 :   eh = curl_easy_init ();
     364            0 :   if ( (NULL == eh) ||
     365              :        (CURLE_OK !=
     366            0 :         curl_easy_setopt (eh,
     367              :                           CURLOPT_URL,
     368            0 :                           rh->request_url)) ||
     369              :        (GNUNET_OK !=
     370            0 :         TALER_curl_easy_post (&rh->post_ctx,
     371              :                               eh,
     372              :                               reg_obj)) )
     373              :   {
     374            0 :     GNUNET_break (0);
     375            0 :     TALER_BANK_registration_cancel (rh);
     376            0 :     if (NULL != eh)
     377            0 :       curl_easy_cleanup (eh);
     378            0 :     json_decref (reg_obj);
     379            0 :     return NULL;
     380              :   }
     381            0 :   json_decref (reg_obj);
     382            0 :   rh->job = GNUNET_CURL_job_add2 (ctx,
     383              :                                   eh,
     384            0 :                                   rh->post_ctx.headers,
     385              :                                   &handle_registration_finished,
     386              :                                   rh);
     387            0 :   if (NULL == rh->job)
     388              :   {
     389            0 :     GNUNET_break (0);
     390            0 :     curl_easy_cleanup (eh);
     391            0 :     TALER_BANK_registration_cancel (rh);
     392            0 :     return NULL;
     393              :   }
     394            0 :   return rh;
     395              : }
     396              : 
     397              : 
     398              : void
     399            0 : TALER_BANK_registration_cancel (
     400              :   struct TALER_BANK_RegistrationHandle *rh)
     401              : {
     402            0 :   if (NULL != rh->job)
     403              :   {
     404            0 :     GNUNET_CURL_job_cancel (rh->job);
     405            0 :     rh->job = NULL;
     406              :   }
     407            0 :   TALER_curl_easy_post_finished (&rh->post_ctx);
     408            0 :   GNUNET_free (rh->request_url);
     409            0 :   GNUNET_free (rh);
     410            0 : }
     411              : 
     412              : 
     413              : /* end of bank_api_registration.c */
        

Generated by: LCOV version 2.0-1