merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

taler-merchant-httpd_post-reports-REPORT_ID.c (7091B)


      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 Affero General Public License for more
     12   details.
     13 
     14   You should have received a copy of the GNU Affero General Public License
     15   along with TALER; see the file COPYING.  If not, see
     16   <http://www.gnu.org/licenses/>
     17 */
     18 /**
     19  * @file src/backend/taler-merchant-httpd_post-reports-REPORT_ID.c
     20  * @brief implementation of POST /reports/$REPORT_ID
     21  * @author Christian Grothoff
     22  */
     23 #include "platform.h"
     24 #include "taler-merchant-httpd_dispatcher.h"
     25 #include "taler-merchant-httpd_post-reports-REPORT_ID.h"
     26 #include <taler/taler_json_lib.h>
     27 #include "merchant-database/check_report.h"
     28 
     29 
     30 enum MHD_Result
     31 TMH_post_reports_ID (
     32   const struct TMH_RequestHandler *rh,
     33   struct MHD_Connection *connection,
     34   struct TMH_HandlerContext *hc)
     35 {
     36   const char *report_id_str = hc->infix;
     37   unsigned long long report_id;
     38   const char *mime_type;
     39   struct TALER_MERCHANT_ReportToken report_token;
     40   struct GNUNET_JSON_Specification spec[] = {
     41     GNUNET_JSON_spec_fixed_auto ("report_token",
     42                                  &report_token),
     43     GNUNET_JSON_spec_end ()
     44   };
     45   enum GNUNET_DB_QueryStatus qs;
     46   char *instance_id;
     47   char *data_source;
     48 
     49   {
     50     char dummy;
     51 
     52     if (1 != sscanf (report_id_str,
     53                      "%llu%c",
     54                      &report_id,
     55                      &dummy))
     56     {
     57       GNUNET_break_op (0);
     58       return TALER_MHD_reply_with_error (connection,
     59                                          MHD_HTTP_BAD_REQUEST,
     60                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
     61                                          "report_id");
     62     }
     63   }
     64 
     65   {
     66     enum GNUNET_GenericReturnValue res;
     67 
     68     res = TALER_MHD_parse_json_data (connection,
     69                                      hc->request_body,
     70                                      spec);
     71     if (GNUNET_OK != res)
     72     {
     73       GNUNET_break_op (0);
     74       return (GNUNET_NO == res)
     75         ? MHD_YES
     76         : MHD_NO;
     77     }
     78   }
     79 
     80   mime_type = MHD_lookup_connection_value (connection,
     81                                            MHD_HEADER_KIND,
     82                                            MHD_HTTP_HEADER_ACCEPT);
     83   if (NULL == mime_type)
     84     mime_type = "application/json";
     85   qs = TALER_MERCHANTDB_check_report (TMH_db,
     86                                       report_id,
     87                                       &report_token,
     88                                       mime_type,
     89                                       &instance_id,
     90                                       &data_source);
     91   if (qs < 0)
     92   {
     93     GNUNET_break (0);
     94     return TALER_MHD_reply_with_error (connection,
     95                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
     96                                        TALER_EC_GENERIC_DB_FETCH_FAILED,
     97                                        "check_report");
     98   }
     99   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    100   {
    101     return TALER_MHD_reply_with_error (connection,
    102                                        MHD_HTTP_NOT_FOUND,
    103                                        TALER_EC_MERCHANT_GENERIC_REPORT_UNKNOWN,
    104                                        report_id_str);
    105   }
    106 
    107   {
    108     struct TMH_MerchantInstance *mi;
    109 
    110     mi = TMH_lookup_instance (instance_id);
    111     if (NULL == mi)
    112     {
    113       /* Strange, we found the report but the instance of the
    114          report is not known. This should basically be impossible
    115          modulo maybe some transactional issue where the
    116          instance was created, the report added *and* triggered
    117          before this process was able to process the notification
    118          about the new instance. Wild. */
    119       GNUNET_break (0);
    120       GNUNET_free (instance_id);
    121       GNUNET_free (data_source);
    122       return TALER_MHD_reply_with_error (connection,
    123                                          MHD_HTTP_NOT_FOUND,
    124                                          TALER_EC_MERCHANT_GENERIC_REPORT_UNKNOWN,
    125                                          report_id_str);
    126     }
    127     /* Rewrite request: force instance to match report */
    128     mi->rc++;
    129     if (NULL != hc->instance)
    130       TMH_instance_decref (hc->instance);
    131     hc->instance = mi;
    132   }
    133   {
    134     enum GNUNET_GenericReturnValue ret;
    135     bool is_public; /* ignored: access control never applies for reports */
    136     char *q;
    137 
    138     /* This rewrites the request: force request handler to match report! */
    139     q = strchr (data_source,
    140                 '?');
    141     if (NULL != q)
    142     {
    143       /* Terminate URI at '?' for dispatching, parse
    144          query parameters and add them to this connection */
    145       *q = '\0';
    146       for (char *tok = strtok (q + 1,
    147                                "&");
    148            NULL != tok;
    149            tok = strtok (NULL,
    150                          "&"))
    151       {
    152         char *eq;
    153 
    154         eq = strchr (tok,
    155                      '=');
    156         if (NULL == eq)
    157         {
    158           GNUNET_break (MHD_YES ==
    159                         MHD_set_connection_value (connection,
    160                                                   MHD_GET_ARGUMENT_KIND,
    161                                                   tok,
    162                                                   NULL));
    163         }
    164         else
    165         {
    166           *eq = '\0';
    167           GNUNET_break (MHD_YES ==
    168                         MHD_set_connection_value (connection,
    169                                                   MHD_GET_ARGUMENT_KIND,
    170                                                   tok,
    171                                                   eq + 1));
    172         }
    173       }
    174     }
    175 
    176     hc->rh = NULL; /* just to make it clear: current one will NOT remain */
    177     /* The query arguments parsed above were registered on the
    178        connection with pointers *into* @e data_source (MHD does not
    179        copy them).  Ownership of @e data_source is therefore handed to
    180        @e hc, which frees it only once the connection is finished; we
    181        must NOT free it here (doing so would leave the connection --
    182        and the re-dispatched GET handler that reads those arguments --
    183        with dangling pointers). */
    184     GNUNET_free (hc->rewritten_url);
    185     hc->rewritten_url = data_source;
    186     ret = TMH_dispatch_request (hc,
    187                                 data_source,
    188                                 MHD_HTTP_METHOD_GET,
    189                                 0 == strcmp ("admin",
    190                                              instance_id),
    191                                 &is_public);
    192     if (GNUNET_OK != ret)
    193     {
    194       GNUNET_free (instance_id);
    195       return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
    196     }
    197     GNUNET_break (NULL != hc->rh);
    198   }
    199   GNUNET_free (instance_id);
    200   /* MHD will call the main handler again, which will now
    201      dispatch into the _new_ handler callback we just installed! */
    202   return MHD_YES;
    203 }