taler-merchant-httpd_post-reports-REPORT_ID.c (6632B)
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 ret = TMH_dispatch_request (hc, 178 data_source, 179 MHD_HTTP_METHOD_GET, 180 0 == strcmp ("admin", 181 instance_id), 182 &is_public); 183 if (GNUNET_OK != ret) 184 { 185 GNUNET_free (instance_id); 186 GNUNET_free (data_source); 187 return (GNUNET_NO == ret) ? MHD_YES : MHD_NO; 188 } 189 GNUNET_break (NULL != hc->rh); 190 } 191 GNUNET_free (instance_id); 192 GNUNET_free (data_source); 193 /* MHD will call the main handler again, which will now 194 dispatch into the _new_ handler callback we just installed! */ 195 return MHD_YES; 196 }