Line data Source code
1 : /*
2 : This file is part of TALER
3 : (C) 2022-2023 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify
6 : it under the terms of the GNU Affero General Public License as
7 : published by the Free Software Foundation; either version 3,
8 : or (at your option) any later version.
9 :
10 : TALER is distributed in the hope that it will be useful, but
11 : WITHOUT ANY WARRANTY; without even the implied warranty of
12 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 : GNU General Public License for more details.
14 :
15 : You should have received a copy of the GNU General Public
16 : License along with TALER; see the file COPYING. If not,
17 : see <http://www.gnu.org/licenses/>
18 : */
19 :
20 : /**
21 : * @file taler-merchant-httpd_post-using-templates.c
22 : * @brief implementing POST /using-templates request handling
23 : * @author Priscilla HUANG
24 : * @author Christian Grothoff
25 : */
26 : #include "platform.h"
27 : #include "taler-merchant-httpd_post-using-templates.h"
28 : #include "taler-merchant-httpd_private-post-orders.h"
29 : #include "taler-merchant-httpd_helper.h"
30 : #include <taler/taler_json_lib.h>
31 :
32 :
33 : /**
34 : * Our context.
35 : */
36 : struct UseContext
37 : {
38 : /**
39 : * Internal handler context we are passing into the
40 : * POST /private/orders handler.
41 : */
42 : struct TMH_HandlerContext ihc;
43 :
44 : /**
45 : * Our template details from the DB.
46 : */
47 : struct TALER_MERCHANTDB_TemplateDetails etp;
48 :
49 : /**
50 : * True once @e etp was initialized.
51 : */
52 : bool have_etp;
53 : };
54 :
55 :
56 : /**
57 : * Clean up a `struct UseContext *`
58 : *
59 : * @param cls a `struct UseContext *`
60 : */
61 : static void
62 14 : cleanup_use_context (void *cls)
63 : {
64 14 : struct UseContext *uc = cls;
65 :
66 14 : TALER_MERCHANTDB_template_details_free (&uc->etp);
67 14 : if (NULL != uc->ihc.cc)
68 4 : uc->ihc.cc (uc->ihc.ctx);
69 14 : json_decref (uc->ihc.request_body);
70 14 : GNUNET_free (uc);
71 14 : }
72 :
73 :
74 : MHD_RESULT
75 18 : TMH_post_using_templates_ID (const struct TMH_RequestHandler *rh,
76 : struct MHD_Connection *connection,
77 : struct TMH_HandlerContext *hc)
78 : {
79 18 : struct TMH_MerchantInstance *mi = hc->instance;
80 18 : const char *template_id = hc->infix;
81 18 : const char *summary = NULL;
82 18 : const char *fulfillment_url = NULL;
83 18 : const char *fulfillment_message = NULL;
84 : struct TALER_Amount amount;
85 : bool no_amount;
86 : bool no_summary;
87 : struct GNUNET_JSON_Specification spec[] = {
88 18 : GNUNET_JSON_spec_mark_optional (
89 : GNUNET_JSON_spec_string ("summary",
90 : &summary),
91 : NULL),
92 18 : GNUNET_JSON_spec_mark_optional (
93 : TALER_JSON_spec_amount_any ("amount",
94 : &amount),
95 : &no_amount),
96 18 : GNUNET_JSON_spec_end ()
97 : };
98 18 : struct UseContext *uc = hc->ctx;
99 :
100 18 : if (NULL == uc)
101 : {
102 14 : uc = GNUNET_new (struct UseContext);
103 14 : hc->ctx = uc;
104 14 : hc->cc = &cleanup_use_context;
105 14 : uc->ihc.instance = hc->instance;
106 : }
107 :
108 : {
109 : enum GNUNET_GenericReturnValue res;
110 :
111 18 : res = TALER_MHD_parse_json_data (connection,
112 18 : hc->request_body,
113 : spec);
114 18 : if (GNUNET_OK != res)
115 : {
116 0 : GNUNET_break_op (0);
117 : return (GNUNET_NO == res)
118 : ? MHD_YES
119 0 : : MHD_NO;
120 : }
121 : }
122 :
123 18 : if (! uc->have_etp)
124 : {
125 : enum GNUNET_DB_QueryStatus qs;
126 :
127 14 : qs = TMH_db->lookup_template (TMH_db->cls,
128 14 : mi->settings.id,
129 : template_id,
130 : &uc->etp);
131 14 : switch (qs)
132 : {
133 0 : case GNUNET_DB_STATUS_HARD_ERROR:
134 : /* Clean up and fail hard */
135 0 : GNUNET_break (0);
136 0 : GNUNET_JSON_parse_free (spec);
137 0 : return TALER_MHD_reply_with_error (
138 : connection,
139 : MHD_HTTP_INTERNAL_SERVER_ERROR,
140 : TALER_EC_GENERIC_DB_FETCH_FAILED,
141 : NULL);
142 0 : case GNUNET_DB_STATUS_SOFT_ERROR:
143 : /* this should be impossible (single select) */
144 0 : GNUNET_break (0);
145 0 : GNUNET_JSON_parse_free (spec);
146 0 : return TALER_MHD_reply_with_error (
147 : connection,
148 : MHD_HTTP_INTERNAL_SERVER_ERROR,
149 : TALER_EC_GENERIC_DB_FETCH_FAILED,
150 : NULL);
151 2 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
152 : /* template not found! */
153 2 : GNUNET_JSON_parse_free (spec);
154 2 : return TALER_MHD_reply_with_error (
155 : connection,
156 : MHD_HTTP_NOT_FOUND,
157 : TALER_EC_MERCHANT_GENERIC_TEMPLATE_UNKNOWN,
158 : template_id);
159 12 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
160 : /* all good */
161 12 : uc->have_etp = true;
162 12 : break;
163 : } /* End of the switch */
164 : }
165 16 : if (NULL == uc->ihc.request_body)
166 : {
167 : /* template */
168 12 : const char *tsummary = NULL;
169 12 : const char *tcurrency = NULL;
170 : uint32_t min_age;
171 : struct GNUNET_TIME_Relative pay_duration;
172 : struct TALER_Amount tamount;
173 : bool no_tamount;
174 : struct GNUNET_JSON_Specification tspec[] = {
175 12 : GNUNET_JSON_spec_mark_optional (
176 : GNUNET_JSON_spec_string ("summary",
177 : &tsummary),
178 : NULL),
179 12 : GNUNET_JSON_spec_mark_optional (
180 : GNUNET_JSON_spec_string ("currency",
181 : &tcurrency),
182 : NULL),
183 12 : GNUNET_JSON_spec_mark_optional (
184 : TALER_JSON_spec_amount_any ("amount",
185 : &tamount),
186 : &no_tamount),
187 12 : GNUNET_JSON_spec_uint32 ("minimum_age",
188 : &min_age),
189 12 : GNUNET_JSON_spec_relative_time ("pay_duration",
190 : &pay_duration),
191 12 : GNUNET_JSON_spec_end ()
192 : };
193 : json_t *fake_body;
194 :
195 : {
196 : enum GNUNET_GenericReturnValue res;
197 : const char *err_name;
198 : unsigned int err_line;
199 :
200 12 : res = GNUNET_JSON_parse (uc->etp.template_contract,
201 : tspec,
202 : &err_name,
203 : &err_line);
204 12 : if (GNUNET_OK != res)
205 : {
206 0 : GNUNET_break (0);
207 0 : json_dumpf (uc->etp.template_contract,
208 : stderr,
209 : JSON_INDENT (2));
210 0 : GNUNET_JSON_parse_free (spec);
211 0 : return TALER_MHD_reply_with_error (
212 : connection,
213 : MHD_HTTP_INTERNAL_SERVER_ERROR,
214 : TALER_EC_GENERIC_DB_FETCH_FAILED,
215 : err_name);
216 : }
217 : }
218 :
219 12 : if ( (! no_amount) &&
220 10 : (! no_tamount) )
221 : {
222 4 : GNUNET_JSON_parse_free (spec);
223 4 : return TALER_MHD_reply_with_error (
224 : connection,
225 : MHD_HTTP_CONFLICT,
226 : TALER_EC_MERCHANT_POST_USING_TEMPLATES_AMOUNT_CONFLICT_TEMPLATES_CONTRACT_AMOUNT,
227 : NULL);
228 : }
229 :
230 8 : if ( (! no_amount) &&
231 6 : (NULL != tcurrency) &&
232 0 : (0 != strcmp (tcurrency,
233 : amount.currency)) )
234 : {
235 0 : GNUNET_JSON_parse_free (spec);
236 0 : return TALER_MHD_reply_with_error (
237 : connection,
238 : MHD_HTTP_CONFLICT,
239 : TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
240 : tcurrency);
241 : }
242 :
243 8 : if (no_amount && no_tamount)
244 : {
245 2 : GNUNET_JSON_parse_free (spec);
246 2 : return TALER_MHD_reply_with_error (
247 : connection,
248 : MHD_HTTP_CONFLICT,
249 : TALER_EC_MERCHANT_POST_USING_TEMPLATES_NO_AMOUNT,
250 : NULL);
251 : }
252 :
253 6 : if ( (NULL != summary) &&
254 4 : (NULL != tsummary) )
255 : {
256 0 : GNUNET_JSON_parse_free (spec);
257 0 : return TALER_MHD_reply_with_error (
258 : connection,
259 : MHD_HTTP_CONFLICT,
260 : TALER_EC_MERCHANT_POST_USING_TEMPLATES_SUMMARY_CONFLICT_TEMPLATES_CONTRACT_SUBJECT,
261 : NULL);
262 : }
263 :
264 6 : if ( (NULL == summary) &&
265 2 : (NULL == tsummary) )
266 : {
267 2 : GNUNET_JSON_parse_free (spec);
268 2 : return TALER_MHD_reply_with_error (
269 : connection,
270 : MHD_HTTP_CONFLICT,
271 : TALER_EC_MERCHANT_POST_USING_TEMPLATES_NO_SUMMARY,
272 : NULL);
273 : }
274 4 : no_summary = (NULL == summary);
275 4 : fake_body = GNUNET_JSON_PACK (
276 : GNUNET_JSON_pack_allow_null (
277 : GNUNET_JSON_pack_string ("otp_id",
278 : uc->etp.otp_id)),
279 : GNUNET_JSON_pack_object_steal (
280 : "order",
281 : GNUNET_JSON_PACK (
282 : TALER_JSON_pack_amount ("amount",
283 : no_amount ?
284 : &tamount :
285 : &amount),
286 : GNUNET_JSON_pack_string ("summary",
287 : no_summary ?
288 : tsummary :
289 : summary),
290 : GNUNET_JSON_pack_allow_null (
291 : GNUNET_JSON_pack_string (
292 : "fulfillment_url",
293 : fulfillment_url)),
294 : GNUNET_JSON_pack_allow_null (
295 : GNUNET_JSON_pack_string (
296 : "fulfillment_message",
297 : fulfillment_message))
298 : ))
299 : );
300 4 : uc->ihc.request_body = fake_body;
301 : }
302 :
303 8 : return TMH_private_post_orders (
304 : NULL, /* not even used */
305 : connection,
306 : &uc->ihc);
307 : }
|