Line data Source code
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 1 : TMH_post_reports_ID (
32 : const struct TMH_RequestHandler *rh,
33 : struct MHD_Connection *connection,
34 : struct TMH_HandlerContext *hc)
35 : {
36 1 : 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 1 : GNUNET_JSON_spec_fixed_auto ("report_token",
42 : &report_token),
43 1 : 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 1 : if (1 != sscanf (report_id_str,
53 : "%llu%c",
54 : &report_id,
55 : &dummy))
56 : {
57 0 : GNUNET_break_op (0);
58 0 : 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 1 : res = TALER_MHD_parse_json_data (connection,
69 1 : hc->request_body,
70 : spec);
71 1 : if (GNUNET_OK != res)
72 : {
73 0 : GNUNET_break_op (0);
74 : return (GNUNET_NO == res)
75 : ? MHD_YES
76 0 : : MHD_NO;
77 : }
78 : }
79 :
80 1 : mime_type = MHD_lookup_connection_value (connection,
81 : MHD_HEADER_KIND,
82 : MHD_HTTP_HEADER_ACCEPT);
83 1 : if (NULL == mime_type)
84 0 : mime_type = "application/json";
85 1 : 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 1 : if (qs < 0)
92 : {
93 0 : GNUNET_break (0);
94 0 : 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 1 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
100 : {
101 0 : 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 1 : mi = TMH_lookup_instance (instance_id);
111 1 : 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 0 : GNUNET_break (0);
120 0 : GNUNET_free (instance_id);
121 0 : GNUNET_free (data_source);
122 0 : 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 1 : mi->rc++;
129 1 : if (NULL != hc->instance)
130 1 : TMH_instance_decref (hc->instance);
131 1 : 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 1 : q = strchr (data_source,
140 : '?');
141 1 : if (NULL != q)
142 : {
143 : /* Terminate URI at '?' for dispatching, parse
144 : query parameters and add them to this connection */
145 0 : *q = '\0';
146 0 : for (char *tok = strtok (q + 1,
147 : "&");
148 0 : NULL != tok;
149 0 : tok = strtok (NULL,
150 : "&"))
151 : {
152 : char *eq;
153 :
154 0 : eq = strchr (tok,
155 : '=');
156 0 : if (NULL == eq)
157 : {
158 : /* Match MHD's normal parsing of request query parameters. */
159 0 : (void) MHD_http_unescape (tok);
160 0 : GNUNET_break (MHD_YES ==
161 : MHD_set_connection_value (connection,
162 : MHD_GET_ARGUMENT_KIND,
163 : tok,
164 : NULL));
165 : }
166 : else
167 : {
168 0 : *eq = '\0';
169 : /* The stored data source contains URL-escaped parameters. */
170 0 : (void) MHD_http_unescape (tok);
171 0 : (void) MHD_http_unescape (eq + 1);
172 0 : 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 1 : 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 1 : GNUNET_free (hc->rewritten_url);
190 1 : hc->rewritten_url = data_source;
191 1 : ret = TMH_dispatch_request (hc,
192 : data_source,
193 : MHD_HTTP_METHOD_GET,
194 1 : 0 == strcmp ("admin",
195 : instance_id),
196 : &is_public);
197 1 : if (GNUNET_OK != ret)
198 : {
199 0 : GNUNET_free (instance_id);
200 0 : return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
201 : }
202 1 : GNUNET_break (NULL != hc->rh);
203 : }
204 1 : 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 1 : return MHD_YES;
208 : }
|