LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_post-private-donau.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 60.7 % 117 71
Test Date: 2026-09-04 23:42:01 Functions: 75.0 % 4 3

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 2024, 2025 Taler Systems SA
       4              : 
       5              :   TALER 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              :   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 <http://www.gnu.org/licenses/>
      15              :  */
      16              : /**
      17              :  * @file src/backend/taler-merchant-httpd_post-private-donau.c
      18              :  * @brief implementation of POST /donau
      19              :  * @author Bohdan Potuzhnyi
      20              :  * @author Vlada Svirsh
      21              :  * @author Christian Grothoff
      22              :  */
      23              : #include "platform.h"
      24              : #include <jansson.h>
      25              : #include "donau/donau_service.h"
      26              : #include <taler/taler_json_lib.h>
      27              : #include <taler/taler_dbevents.h>
      28              : #include "taler/taler_merchant_service.h"
      29              : #include "taler-merchant-httpd_post-private-donau.h"
      30              : #include "merchant-database/get_exists_donau_instance.h"
      31              : #include "merchant-database/insert_donau_instance.h"
      32              : #include "merchant-database/event_listen.h"
      33              : #include "merchant-database/event_notify.h"
      34              : #include "merchant-database/set_instance.h"
      35              : 
      36              : 
      37              : /**
      38              :  * Context for the POST /donau request handler.
      39              :  */
      40              : struct PostDonauCtx
      41              : {
      42              :   /**
      43              :    * Stored in a DLL.
      44              :    */
      45              :   struct PostDonauCtx *next;
      46              : 
      47              :   /**
      48              :    * Stored in a DLL.
      49              :    */
      50              :   struct PostDonauCtx *prev;
      51              : 
      52              :   /**
      53              :    * Connection to the MHD server
      54              :    */
      55              :   struct MHD_Connection *connection;
      56              : 
      57              :   /**
      58              :    * Context of the request handler.
      59              :    */
      60              :   struct TMH_HandlerContext *hc;
      61              : 
      62              :   /**
      63              :    * URL of the DONAU service
      64              :    * to which the charity belongs.
      65              :    */
      66              :   const char *donau_url;
      67              : 
      68              :   /**
      69              :    * ID of the charity in the DONAU service.
      70              :    */
      71              :   uint64_t charity_id;
      72              : 
      73              :   /**
      74              :    * Handle returned by DONAU_charities_get(); needed to cancel on
      75              :    * connection abort, etc.
      76              :    */
      77              :   struct DONAU_CharityGetHandle *get_handle;
      78              : 
      79              :   /**
      80              :    * Response to return.
      81              :    */
      82              :   struct MHD_Response *response;
      83              : 
      84              :   /**
      85              :    * HTTP status for @e response.
      86              :    */
      87              :   unsigned int http_status;
      88              : 
      89              :   /**
      90              :    * #GNUNET_YES if we are suspended,
      91              :    * #GNUNET_NO if not,
      92              :    * #GNUNET_SYSERR on shutdown
      93              :    */
      94              :   enum GNUNET_GenericReturnValue suspended;
      95              : };
      96              : 
      97              : 
      98              : /**
      99              :  * Head of active pay context DLL.
     100              :  */
     101              : static struct PostDonauCtx *pdc_head;
     102              : 
     103              : /**
     104              :  * Tail of active pay context DLL.
     105              :  */
     106              : static struct PostDonauCtx *pdc_tail;
     107              : 
     108              : 
     109              : void
     110            0 : TMH_force_post_donau_resume ()
     111              : {
     112            0 :   for (struct PostDonauCtx *pdc = pdc_head;
     113            0 :        NULL != pdc;
     114            0 :        pdc = pdc->next)
     115              :   {
     116            0 :     if (GNUNET_YES == pdc->suspended)
     117              :     {
     118            0 :       pdc->suspended = GNUNET_SYSERR;
     119            0 :       MHD_resume_connection (pdc->connection);
     120              :     }
     121              :   }
     122            0 : }
     123              : 
     124              : 
     125              : /**
     126              :  * Callback for DONAU_charities_get() to handle the response.
     127              :  *
     128              :  * @param cls closure with PostDonauCtx
     129              :  * @param gcr response from Donau
     130              :  */
     131              : static void
     132            2 : donau_charity_get_cb (void *cls,
     133              :                       const struct DONAU_GetCharityResponse *gcr)
     134              : {
     135            2 :   struct PostDonauCtx *pdc = cls;
     136              :   enum GNUNET_DB_QueryStatus qs;
     137              : 
     138            2 :   pdc->get_handle = NULL;
     139            2 :   pdc->suspended = GNUNET_NO;
     140            2 :   MHD_resume_connection (pdc->connection);
     141            2 :   TALER_MHD_daemon_trigger ();
     142            2 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     143              :               "Processing DONAU charity get response");
     144              :   /* Anything but 200 => propagate Donau’s response. */
     145            2 :   if (MHD_HTTP_OK != gcr->hr.http_status)
     146              :   {
     147            0 :     pdc->http_status = MHD_HTTP_BAD_GATEWAY;
     148            0 :     pdc->response = TALER_MHD_MAKE_JSON_PACK (
     149              :       TALER_MHD_PACK_EC (gcr->hr.ec),
     150              :       GNUNET_JSON_pack_uint64 ("donau_http_status",
     151              :                                gcr->hr.http_status));
     152            0 :     return;
     153              :   }
     154              : 
     155            2 :   if (0 !=
     156            2 :       GNUNET_memcmp (&gcr->details.ok.charity.charity_pub.eddsa_pub,
     157              :                      &pdc->hc->instance->merchant_pub.eddsa_pub))
     158              :   {
     159            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     160              :                 "Charity key at donau does not match our merchant key\n");
     161            0 :     pdc->http_status = MHD_HTTP_CONFLICT;
     162            0 :     pdc->response = TALER_MHD_make_error (
     163              :       TALER_EC_GENERIC_PARAMETER_MALFORMED,
     164              :       "charity_pub != merchant_pub");
     165            0 :     return;
     166              :   }
     167              : 
     168            2 :   qs = TALER_MERCHANTDB_set_instance (
     169              :     TMH_db,
     170            2 :     pdc->hc->instance->settings.id);
     171            2 :   if (0 >= qs)
     172              :   {
     173            0 :     GNUNET_break (0);
     174            0 :     pdc->http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
     175            0 :     pdc->response = TALER_MHD_make_error (
     176              :       TALER_EC_GENERIC_DB_STORE_FAILED,
     177              :       "set_instance");
     178            0 :     return;
     179              :   }
     180              : 
     181            2 :   qs = TALER_MERCHANTDB_insert_donau_instance (TMH_db,
     182              :                                                pdc->donau_url,
     183              :                                                &gcr->details.ok.charity,
     184              :                                                pdc->charity_id);
     185            2 :   GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
     186              :                 TALER_MERCHANTDB_set_instance (
     187              :                   TMH_db,
     188              :                   NULL));
     189            2 :   switch (qs)
     190              :   {
     191            0 :   case GNUNET_DB_STATUS_HARD_ERROR:
     192            0 :     GNUNET_break (0);
     193            0 :     pdc->http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
     194            0 :     pdc->response = TALER_MHD_make_error (
     195              :       TALER_EC_GENERIC_DB_STORE_FAILED,
     196              :       "insert_donau_instance");
     197            0 :     break;
     198            0 :   case GNUNET_DB_STATUS_SOFT_ERROR:
     199            0 :     GNUNET_break (0);
     200            0 :     pdc->http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
     201            0 :     pdc->response = TALER_MHD_make_error (
     202              :       TALER_EC_GENERIC_DB_STORE_FAILED,
     203              :       "insert_donau_instance");
     204            0 :     break;
     205            0 :   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
     206              :     /* presumably idempotent + concurrent, no need to notify, but still respond */
     207            0 :     pdc->http_status = MHD_HTTP_NO_CONTENT;
     208            0 :     pdc->response = MHD_create_response_from_buffer_static (0,
     209              :                                                             NULL);
     210            0 :     TALER_MHD_add_global_headers (pdc->response,
     211              :                                   false);
     212            0 :     break;
     213            2 :   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
     214              :     {
     215            2 :       struct GNUNET_DB_EventHeaderP es = {
     216            2 :         .size = htons (sizeof (es)),
     217            2 :         .type = htons (TALER_DBEVENT_MERCHANT_DONAU_KEYS)
     218              :       };
     219              : 
     220            2 :       TALER_MERCHANTDB_event_notify (TMH_db,
     221              :                                      &es,
     222            2 :                                      pdc->donau_url,
     223            2 :                                      strlen (pdc->donau_url) + 1);
     224            2 :       pdc->http_status = MHD_HTTP_NO_CONTENT;
     225            2 :       pdc->response = MHD_create_response_from_buffer_static (0,
     226              :                                                               NULL);
     227            2 :       TALER_MHD_add_global_headers (pdc->response,
     228              :                                     false);
     229            2 :       break;
     230              :     }
     231              :   }
     232              : }
     233              : 
     234              : 
     235              : /**
     236              :  * Cleanup function for the PostDonauCtx.
     237              :  *
     238              :  * @param cls closure with PostDonauCtx
     239              :  */
     240              : static void
     241            4 : post_donau_cleanup (void *cls)
     242              : {
     243            4 :   struct PostDonauCtx *pdc = cls;
     244              : 
     245            4 :   if (pdc->get_handle)
     246              :   {
     247            0 :     DONAU_charity_get_cancel (pdc->get_handle);
     248            0 :     pdc->get_handle = NULL;
     249              :   }
     250            4 :   GNUNET_CONTAINER_DLL_remove (pdc_head,
     251              :                                pdc_tail,
     252              :                                pdc);
     253            4 :   GNUNET_free (pdc);
     254            4 : }
     255              : 
     256              : 
     257              : /**
     258              :  * Handle a POST "/donau" request.
     259              :  *
     260              :  * @param rh context of the handler
     261              :  * @param connection the MHD connection to handle
     262              :  * @param[in,out] hc context with further information about the request
     263              :  * @return MHD result code
     264              :  */
     265              : enum MHD_Result
     266            6 : TMH_private_post_donau_instance (const struct TMH_RequestHandler *rh,
     267              :                                  struct MHD_Connection *connection,
     268              :                                  struct TMH_HandlerContext *hc)
     269              : {
     270            6 :   struct PostDonauCtx *pdc = hc->ctx;
     271              : 
     272            6 :   if (NULL == pdc)
     273              :   {
     274              :     enum GNUNET_DB_QueryStatus qs;
     275              : 
     276            4 :     pdc = GNUNET_new (struct PostDonauCtx);
     277            4 :     pdc->connection = connection;
     278            4 :     pdc->hc = hc;
     279            4 :     hc->ctx = pdc;
     280            4 :     hc->cc = &post_donau_cleanup;
     281            4 :     GNUNET_CONTAINER_DLL_insert (pdc_head,
     282              :                                  pdc_tail,
     283              :                                  pdc);
     284              :     {
     285              :       struct GNUNET_JSON_Specification spec[] = {
     286            4 :         TALER_JSON_spec_web_url ("donau_url",
     287              :                                  &pdc->donau_url),
     288            4 :         GNUNET_JSON_spec_uint64 ("charity_id",
     289              :                                  &pdc->charity_id),
     290            4 :         GNUNET_JSON_spec_end ()
     291              :       };
     292              : 
     293              :       enum GNUNET_GenericReturnValue res;
     294              : 
     295            4 :       res = TALER_MHD_parse_json_data (connection,
     296            4 :                                        hc->request_body,
     297              :                                        spec);
     298            4 :       if (GNUNET_OK != res)
     299              :       {
     300              :         /* On a client error the parser already queued a 400 and
     301              :            returns GNUNET_NO; we must return MHD_YES so MHD sends it,
     302              :            returning MHD_NO only on GNUNET_SYSERR (drop connection). */
     303            0 :         GNUNET_break_op (0);
     304              :         return (GNUNET_NO == res)
     305              :           ? MHD_YES
     306            0 :           : MHD_NO;
     307              :       }
     308              :     }
     309            4 :     qs = TALER_MERCHANTDB_get_exists_donau_instance (TMH_db,
     310            4 :                                                      &hc->instance->merchant_pub,
     311              :                                                      pdc->donau_url,
     312              :                                                      pdc->charity_id);
     313            4 :     switch (qs)
     314              :     {
     315            0 :     case GNUNET_DB_STATUS_HARD_ERROR:
     316              :     case GNUNET_DB_STATUS_SOFT_ERROR:
     317            0 :       GNUNET_break (0);
     318            0 :       pdc->http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
     319            0 :       return TALER_MHD_reply_with_error (
     320              :         connection,
     321              :         MHD_HTTP_INTERNAL_SERVER_ERROR,
     322              :         TALER_EC_GENERIC_DB_FETCH_FAILED,
     323              :         "get_exists_donau_instance");
     324              :       break;
     325            2 :     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
     326            2 :       pdc->http_status = MHD_HTTP_NO_CONTENT;
     327            2 :       pdc->response = MHD_create_response_from_buffer_static (0,
     328              :                                                               NULL);
     329            2 :       TALER_MHD_add_global_headers (pdc->response,
     330              :                                     false);
     331            2 :       goto respond;
     332            2 :     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
     333              :       /* normal case, continue below */
     334            2 :       break;
     335              :     }
     336              : 
     337              :     {
     338              :       struct DONAU_CharityPrivateKeyP cp;
     339              : 
     340              :       /* Merchant private key IS our charity private key */
     341            2 :       cp.eddsa_priv = hc->instance->merchant_priv.eddsa_priv;
     342            2 :       pdc->get_handle =
     343            2 :         DONAU_charity_get (TMH_curl_ctx,
     344              :                            pdc->donau_url,
     345              :                            pdc->charity_id,
     346              :                            &cp,
     347              :                            &donau_charity_get_cb,
     348              :                            pdc);
     349              :     }
     350            2 :     if (NULL == pdc->get_handle)
     351              :     {
     352            0 :       GNUNET_break (0);
     353            0 :       return TALER_MHD_reply_with_error (connection,
     354              :                                          MHD_HTTP_SERVICE_UNAVAILABLE,
     355              :                                          TALER_EC_GENERIC_ALLOCATION_FAILURE,
     356              :                                          "Failed to initiate Donau lookup");
     357              :     }
     358            2 :     pdc->suspended = GNUNET_YES;
     359            2 :     MHD_suspend_connection (connection);
     360            2 :     return MHD_YES;
     361              :   }
     362            2 : respond:
     363            4 :   if (NULL != pdc->response)
     364              :   {
     365              :     enum MHD_Result res;
     366              : 
     367            4 :     GNUNET_break (GNUNET_NO == pdc->suspended);
     368            4 :     res = MHD_queue_response (pdc->connection,
     369              :                               pdc->http_status,
     370              :                               pdc->response);
     371            4 :     MHD_destroy_response (pdc->response);
     372            4 :     return res;
     373              :   }
     374            0 :   GNUNET_break (GNUNET_SYSERR == pdc->suspended);
     375            0 :   return MHD_NO;
     376              : }
        

Generated by: LCOV version 2.0-1