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 }