merchant

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

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


      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/get_report_by_token.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_get_report_by_token (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                                        "get_report_by_token");
     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           /* Match MHD's normal parsing of request query parameters. */
    159           (void) MHD_http_unescape (tok);
    160           GNUNET_break (MHD_YES ==
    161                         MHD_set_connection_value (connection,
    162                                                   MHD_GET_ARGUMENT_KIND,
    163                                                   tok,
    164                                                   NULL));
    165         }
    166         else
    167         {
    168           *eq = '\0';
    169           /* The stored data source contains URL-escaped parameters. */
    170           (void) MHD_http_unescape (tok);
    171           (void) MHD_http_unescape (eq + 1);
    172           GNUNET_break (MHD_YES ==
    173                         MHD_set_connection_value (connection,
    174                                                   MHD_GET_ARGUMENT_KIND,
    175                                                   tok,
    176                                                   eq + 1));
    177         }
    178       }
    179     }
    180 
    181     hc->rh = NULL; /* just to make it clear: current one will NOT remain */
    182     /* The query arguments parsed above were registered on the
    183        connection with pointers *into* @e data_source (MHD does not
    184        copy them).  Ownership of @e data_source is therefore handed to
    185        @e hc, which frees it only once the connection is finished; we
    186        must NOT free it here (doing so would leave the connection --
    187        and the re-dispatched GET handler that reads those arguments --
    188        with dangling pointers). */
    189     GNUNET_free (hc->rewritten_url);
    190     hc->rewritten_url = data_source;
    191     ret = TMH_dispatch_request (hc,
    192                                 data_source,
    193                                 MHD_HTTP_METHOD_GET,
    194                                 0 == strcmp ("admin",
    195                                              instance_id),
    196                                 &is_public);
    197     if (GNUNET_OK != ret)
    198     {
    199       GNUNET_free (instance_id);
    200       return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
    201     }
    202     GNUNET_break (NULL != hc->rh);
    203   }
    204   GNUNET_free (instance_id);
    205   /* MHD will call the main handler again, which will now
    206      dispatch into the _new_ handler callback we just installed! */
    207   return MHD_YES;
    208 }