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 }