LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_get-sessions-SESSION_ID.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 3.5 % 85 3
Test Date: 2026-09-04 23:42:01 Functions: 25.0 % 4 1

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   (C) 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_get-sessions-SESSION_ID.c
      18              :  * @brief implement GET /session/$ID
      19              :  * @author Christian Grothoff
      20              :  */
      21              : #include "platform.h"
      22              : #include "taler-merchant-httpd_get-sessions-SESSION_ID.h"
      23              : #include <taler/taler_json_lib.h>
      24              : #include <taler/taler_dbevents.h>
      25              : #include "merchant-database/get_order_by_fulfillment.h"
      26              : #include "merchant-database/get_order_status.h"
      27              : #include "merchant-database/event_listen.h"
      28              : 
      29              : 
      30              : /**
      31              :  * Context for a get sessions request.
      32              :  */
      33              : struct GetSessionContext
      34              : {
      35              :   /**
      36              :    * Kept in a DLL.
      37              :    */
      38              :   struct GetSessionContext *next;
      39              : 
      40              :   /**
      41              :    * Kept in a DLL.
      42              :    */
      43              :   struct GetSessionContext *prev;
      44              : 
      45              :   /**
      46              :    * Request context.
      47              :    */
      48              :   struct TMH_HandlerContext *hc;
      49              : 
      50              :   /**
      51              :    * Entry in the #resume_timeout_heap for this check payment, if we are
      52              :    * suspended.
      53              :    */
      54              :   struct TMH_SuspendedConnection sc;
      55              : 
      56              :   /**
      57              :    * Fulfillment URL from the HTTP request.
      58              :    */
      59              :   const char *fulfillment_url;
      60              : 
      61              :   /**
      62              :    * Database event we are waiting on to be resuming on payment.
      63              :    */
      64              :   struct GNUNET_DB_EventHandler *eh;
      65              : 
      66              :   /**
      67              :    * Did we suspend @a connection and are thus in
      68              :    * the #gsc_head DLL (#GNUNET_YES). Set to
      69              :    * #GNUNET_NO if we are not suspended, and to
      70              :    * #GNUNET_SYSERR if we should close the connection
      71              :    * without a response due to shutdown.
      72              :    */
      73              :   enum GNUNET_GenericReturnValue suspended;
      74              : };
      75              : 
      76              : 
      77              : /**
      78              :  * Kept in a DLL.
      79              :  */
      80              : static struct GetSessionContext *gsc_head;
      81              : 
      82              : /**
      83              :  * Kept in a DLL.
      84              :  */
      85              : static struct GetSessionContext *gsc_tail;
      86              : 
      87              : 
      88              : void
      89           20 : TMH_force_get_sessions_ID_resume (void)
      90              : {
      91              :   struct GetSessionContext *gsc;
      92              : 
      93           20 :   while (NULL != (gsc = gsc_head))
      94              :   {
      95            0 :     GNUNET_CONTAINER_DLL_remove (gsc_head,
      96              :                                  gsc_tail,
      97              :                                  gsc);
      98            0 :     gsc->suspended = GNUNET_SYSERR;
      99            0 :     MHD_resume_connection (gsc->sc.con);
     100            0 :     TALER_MHD_daemon_trigger ();   /* we resumed, kick MHD */
     101              :   }
     102           20 : }
     103              : 
     104              : 
     105              : /**
     106              :  * Cleanup helper for TMH_get_session_ID().
     107              :  *
     108              :  * @param cls must be a `struct GetSessionContext`
     109              :  */
     110              : static void
     111            0 : gsc_cleanup (void *cls)
     112              : {
     113            0 :   struct GetSessionContext *gsc = cls;
     114              : 
     115            0 :   if (NULL != gsc->eh)
     116              :   {
     117            0 :     TALER_MERCHANTDB_event_listen_cancel (gsc->eh);
     118            0 :     gsc->eh = NULL;
     119              :   }
     120            0 :   GNUNET_free (gsc);
     121            0 : }
     122              : 
     123              : 
     124              : /**
     125              :  * We have received a trigger from the database
     126              :  * that we should (possibly) resume the request.
     127              :  *
     128              :  * @param cls a `struct GetOrderData` to resume
     129              :  * @param extra string encoding refund amount (or NULL)
     130              :  * @param extra_size number of bytes in @a extra
     131              :  */
     132              : static void
     133            0 : resume_by_event (void *cls,
     134              :                  const void *extra,
     135              :                  size_t extra_size)
     136              : {
     137            0 :   struct GetSessionContext *gsc = cls;
     138              : 
     139            0 :   if (GNUNET_YES != gsc->suspended)
     140              :   {
     141            0 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     142              :                 "Not suspended, ignoring event\n");
     143            0 :     return; /* duplicate event is possible */
     144              :   }
     145            0 :   gsc->suspended = GNUNET_NO;
     146            0 :   GNUNET_CONTAINER_DLL_remove (gsc_head,
     147              :                                gsc_tail,
     148              :                                gsc);
     149            0 :   MHD_resume_connection (gsc->sc.con);
     150            0 :   TALER_MHD_daemon_trigger ();   /* we resumed, kick MHD */
     151              : }
     152              : 
     153              : 
     154              : enum MHD_Result
     155            0 : TMH_get_sessions_ID (
     156              :   const struct TMH_RequestHandler *rh,
     157              :   struct MHD_Connection *connection,
     158              :   struct TMH_HandlerContext *hc)
     159              : {
     160            0 :   struct GetSessionContext *gsc = hc->ctx;
     161            0 :   struct TMH_MerchantInstance *mi = hc->instance;
     162            0 :   char *order_id = NULL;
     163            0 :   bool paid = false;
     164              :   bool is_past;
     165              : 
     166            0 :   GNUNET_assert (NULL != mi);
     167            0 :   if (NULL == gsc)
     168              :   {
     169            0 :     gsc = GNUNET_new (struct GetSessionContext);
     170            0 :     gsc->hc = hc;
     171            0 :     hc->ctx = gsc;
     172            0 :     hc->cc = &gsc_cleanup;
     173            0 :     gsc->sc.con = connection;
     174            0 :     gsc->fulfillment_url = MHD_lookup_connection_value (
     175              :       connection,
     176              :       MHD_GET_ARGUMENT_KIND,
     177              :       "fulfillment_url");
     178            0 :     if (NULL == gsc->fulfillment_url)
     179              :     {
     180            0 :       GNUNET_break_op (0);
     181            0 :       return TALER_MHD_reply_with_error (connection,
     182              :                                          MHD_HTTP_BAD_REQUEST,
     183              :                                          TALER_EC_GENERIC_PARAMETER_MISSING,
     184              :                                          "fulfillment_url");
     185              :     }
     186            0 :     if (! TALER_is_web_url (gsc->fulfillment_url))
     187              :     {
     188            0 :       GNUNET_break_op (0);
     189            0 :       return TALER_MHD_reply_with_error (connection,
     190              :                                          MHD_HTTP_BAD_REQUEST,
     191              :                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
     192              :                                          "fulfillment_url");
     193              :     }
     194              : 
     195            0 :     TALER_MHD_parse_request_timeout (connection,
     196              :                                      &gsc->sc.long_poll_timeout);
     197              : 
     198            0 :     if (! GNUNET_TIME_absolute_is_past (gsc->sc.long_poll_timeout) )
     199              :     {
     200            0 :       struct TMH_SessionEventP session_eh = {
     201            0 :         .header.size = htons (sizeof (session_eh)),
     202            0 :         .header.type = htons (TALER_DBEVENT_MERCHANT_SESSION_CAPTURED),
     203            0 :         .merchant_pub = gsc->hc->instance->merchant_pub
     204              :       };
     205              : 
     206            0 :       GNUNET_CRYPTO_hash (hc->infix,
     207            0 :                           strlen (hc->infix),
     208              :                           &session_eh.h_session_id);
     209            0 :       GNUNET_CRYPTO_hash (gsc->fulfillment_url,
     210              :                           strlen (gsc->fulfillment_url),
     211              :                           &session_eh.h_fulfillment_url);
     212              :       gsc->eh
     213            0 :         = TALER_MERCHANTDB_event_listen (
     214              :             TMH_db,
     215              :             &session_eh.header,
     216              :             GNUNET_TIME_absolute_get_remaining (gsc->sc.long_poll_timeout),
     217              :             &resume_by_event,
     218              :             gsc);
     219              :     }
     220              :   } /* end first-time initialization (NULL == gsc) */
     221              : 
     222            0 :   if (GNUNET_SYSERR == gsc->suspended)
     223            0 :     return MHD_NO; /* close connection on service shutdown */
     224              : 
     225            0 :   is_past = GNUNET_TIME_absolute_is_past (gsc->sc.long_poll_timeout);
     226              :   /* figure out order_id */
     227              :   {
     228              :     enum GNUNET_DB_QueryStatus qs;
     229              : 
     230            0 :     qs = TALER_MERCHANTDB_get_order_by_fulfillment (TMH_db,
     231            0 :                                                     mi->settings.id,
     232              :                                                     gsc->fulfillment_url,
     233            0 :                                                     hc->infix,
     234              :                                                     false,
     235              :                                                     &order_id);
     236            0 :     if (0 > qs)
     237              :     {
     238            0 :       GNUNET_break (0);
     239            0 :       return TALER_MHD_reply_with_error (
     240              :         connection,
     241              :         MHD_HTTP_INTERNAL_SERVER_ERROR,
     242              :         TALER_EC_GENERIC_DB_FETCH_FAILED,
     243              :         "get_order_by_fulfillment");
     244              :     }
     245            0 :     if ( (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) &&
     246              :          is_past)
     247              :     {
     248            0 :       return TALER_MHD_reply_with_error (
     249              :         connection,
     250              :         MHD_HTTP_NOT_FOUND,
     251              :         TALER_EC_MERCHANT_GENERIC_SESSION_UNKNOWN,
     252            0 :         hc->infix);
     253              :     }
     254              :   }
     255              : 
     256              :   /* Check if paid */
     257            0 :   if (NULL != order_id)
     258              :   {
     259              :     enum GNUNET_DB_QueryStatus qs;
     260              :     struct TALER_PrivateContractHashP h_contract_terms;
     261              : 
     262            0 :     qs = TALER_MERCHANTDB_get_order_status (TMH_db,
     263            0 :                                             mi->settings.id,
     264              :                                             order_id,
     265              :                                             &h_contract_terms,
     266              :                                             &paid);
     267            0 :     if (0 >= qs)
     268              :     {
     269            0 :       GNUNET_break (0);
     270            0 :       GNUNET_free (order_id);
     271            0 :       return TALER_MHD_reply_with_error (
     272              :         connection,
     273              :         MHD_HTTP_INTERNAL_SERVER_ERROR,
     274              :         TALER_EC_GENERIC_DB_FETCH_FAILED,
     275              :         "get_order_status");
     276              :     }
     277              :   }
     278              : 
     279            0 :   if (paid)
     280              :   {
     281              :     enum MHD_Result ret;
     282              : 
     283            0 :     ret = TALER_MHD_REPLY_JSON_PACK (
     284              :       connection,
     285              :       MHD_HTTP_OK,
     286              :       GNUNET_JSON_pack_string ("order_id",
     287              :                                order_id));
     288            0 :     GNUNET_free (order_id);
     289            0 :     return ret;
     290              :   }
     291              : 
     292            0 :   if (is_past)
     293              :   {
     294              :     enum MHD_Result ret;
     295              : 
     296            0 :     GNUNET_assert (NULL != order_id);
     297            0 :     ret = TALER_MHD_REPLY_JSON_PACK (
     298              :       connection,
     299              :       MHD_HTTP_ACCEPTED,
     300              :       GNUNET_JSON_pack_string ("order_id",
     301              :                                order_id));
     302            0 :     GNUNET_free (order_id);
     303            0 :     return ret;
     304              :   }
     305              : 
     306            0 :   GNUNET_free (order_id);
     307            0 :   GNUNET_CONTAINER_DLL_insert (gsc_head,
     308              :                                gsc_tail,
     309              :                                gsc);
     310            0 :   gsc->suspended = GNUNET_YES;
     311            0 :   MHD_suspend_connection (gsc->sc.con);
     312            0 :   return MHD_YES;
     313              : }
     314              : 
     315              : 
     316              : /* end of taler-merchant-httpd_get-sessions-SESSION_ID.c */
        

Generated by: LCOV version 2.0-1