Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2014-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 General Public License as
7 : published by the Free Software Foundation; either version 3, or
8 : (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, see
17 : <http://www.gnu.org/licenses/>
18 : */
19 : /**
20 : * @file testing_api_cmd_abort_order.c
21 : * @brief command to test the abort feature.
22 : * @author Marcello Stanisci
23 : */
24 : #include "platform.h"
25 : #include <taler/taler_exchange_service.h>
26 : #include <taler/taler_testing_lib.h>
27 : #include <taler/taler_signatures.h>
28 : #include "taler_merchant_service.h"
29 : #include "taler_merchant_testing_lib.h"
30 :
31 : /**
32 : * State for a " abort" CMD.
33 : */
34 : struct AbortState
35 : {
36 :
37 : /**
38 : * Reference to the "pay" command to abort.
39 : */
40 : const char *pay_reference;
41 :
42 : /**
43 : * Merchant URL.
44 : */
45 : const char *merchant_url;
46 :
47 : /**
48 : * Handle to a "abort" operation.
49 : */
50 : struct TALER_MERCHANT_OrderAbortHandle *oah;
51 :
52 : /**
53 : * Interpreter state.
54 : */
55 : struct TALER_TESTING_Interpreter *is;
56 :
57 : /**
58 : * The actual abort/refund data.
59 : */
60 : struct TALER_MERCHANT_AbortedCoin *acs;
61 :
62 : /**
63 : * Expected HTTP response code.
64 : */
65 : unsigned int http_status;
66 :
67 : /**
68 : * How many refund permissions this CMD got
69 : * the right for. Roughly, there is one refund
70 : * permission for one coin.
71 : */
72 : unsigned int acs_length;
73 :
74 : };
75 :
76 :
77 : /**
78 : * Parse the @a coins specification and grow the @a ac
79 : * array with the coins found, updating @a nac.
80 : *
81 : * @param[in,out] ac pointer to array of coins found
82 : * @param[in,out] nac length of array at @a pc
83 : * @param[in] coins string specifying coins to add to @a pc,
84 : * clobbered in the process
85 : * @param is interpreter state
86 : * @return #GNUNET_OK on success
87 : */
88 : static enum GNUNET_GenericReturnValue
89 2 : build_coins (struct TALER_MERCHANT_AbortCoin **ac,
90 : unsigned int *nac,
91 : char *coins,
92 : struct TALER_TESTING_Interpreter *is)
93 : {
94 2 : for (char *token = strtok (coins, ";");
95 4 : NULL != token;
96 2 : token = strtok (NULL, ";"))
97 : {
98 : char *ctok;
99 : unsigned int ci;
100 : struct TALER_MERCHANT_AbortCoin *icoin;
101 :
102 : /* Token syntax is "LABEL[/NUMBER]" */
103 2 : ctok = strchr (token, '/');
104 2 : ci = 0;
105 2 : if (NULL != ctok)
106 : {
107 0 : *ctok = '\0';
108 0 : ctok++;
109 0 : if (1 != sscanf (ctok,
110 : "%u",
111 : &ci))
112 : {
113 0 : GNUNET_break (0);
114 0 : return GNUNET_SYSERR;
115 : }
116 : }
117 : {
118 : const struct TALER_TESTING_Command *coin_cmd;
119 :
120 2 : coin_cmd = TALER_TESTING_interpreter_lookup_command (is,
121 : token);
122 2 : if (NULL == coin_cmd)
123 : {
124 0 : GNUNET_break (0);
125 0 : return GNUNET_SYSERR;
126 : }
127 2 : GNUNET_array_grow (*ac,
128 : *nac,
129 : (*nac) + 1);
130 2 : icoin = &((*ac)[(*nac) - 1]);
131 :
132 : {
133 : const struct TALER_CoinSpendPrivateKeyP *coin_priv;
134 :
135 2 : GNUNET_assert (GNUNET_OK ==
136 : TALER_TESTING_get_trait_coin_priv (coin_cmd,
137 : ci,
138 : &coin_priv));
139 2 : GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv,
140 : &icoin->coin_pub.eddsa_pub);
141 : }
142 2 : GNUNET_assert (GNUNET_OK ==
143 : TALER_TESTING_get_trait_exchange_url (coin_cmd,
144 : &icoin->exchange_url));
145 : {
146 : const struct TALER_Amount *denom_value;
147 :
148 2 : GNUNET_assert (GNUNET_OK ==
149 : TALER_TESTING_get_trait_amount (coin_cmd,
150 : &denom_value));
151 2 : icoin->amount_with_fee = *denom_value;
152 : }
153 :
154 : }
155 : }
156 2 : return GNUNET_OK;
157 : }
158 :
159 :
160 : /**
161 : * Callback for a "pay abort" operation. Mainly, check HTTP
162 : * response code was as expected and stores refund permissions
163 : * in the state.
164 : *
165 : * @param cls closure.
166 : * @param ar response
167 : */
168 : static void
169 2 : abort_cb (void *cls,
170 : const struct TALER_MERCHANT_AbortResponse *ar)
171 : {
172 2 : struct AbortState *as = cls;
173 :
174 2 : as->oah = NULL;
175 2 : if (as->http_status != ar->hr.http_status)
176 : {
177 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
178 : "Unexpected response code %u (%d) to command `%s' (expected %u)\n",
179 : ar->hr.http_status,
180 : (int) ar->hr.ec,
181 : TALER_TESTING_interpreter_get_current_label (as->is),
182 : as->http_status);
183 0 : TALER_TESTING_FAIL (as->is);
184 : }
185 2 : if ( (MHD_HTTP_OK == ar->hr.http_status) &&
186 2 : (TALER_EC_NONE == ar->hr.ec) )
187 : {
188 2 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
189 : "Received %u refunds\n",
190 : ar->details.ok.num_aborts);
191 2 : as->acs_length = ar->details.ok.num_aborts;
192 2 : as->acs = GNUNET_new_array (as->acs_length,
193 : struct TALER_MERCHANT_AbortedCoin);
194 2 : GNUNET_memcpy (as->acs,
195 : ar->details.ok.aborts,
196 : as->acs_length
197 : * sizeof (struct TALER_MERCHANT_AbortedCoin));
198 : }
199 2 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
200 : "Successful pay-abort (HTTP status: %u)\n",
201 : ar->hr.http_status);
202 2 : TALER_TESTING_interpreter_next (as->is);
203 : }
204 :
205 :
206 : /**
207 : * Run an "abort" CMD.
208 : *
209 : * @param cls closure
210 : * @param cmd command being run.
211 : * @param is interpreter state
212 : */
213 : static void
214 2 : abort_run (void *cls,
215 : const struct TALER_TESTING_Command *cmd,
216 : struct TALER_TESTING_Interpreter *is)
217 : {
218 2 : struct AbortState *as = cls;
219 : const struct TALER_TESTING_Command *pay_cmd;
220 : const char *proposal_reference;
221 : const char *coin_reference;
222 : const struct TALER_TESTING_Command *proposal_cmd;
223 : const char *order_id;
224 : const struct TALER_PrivateContractHashP *h_proposal;
225 : struct TALER_MerchantPublicKeyP merchant_pub;
226 : struct TALER_Amount total_amount;
227 : const char *error_name;
228 : unsigned int error_line;
229 : struct TALER_MERCHANT_AbortCoin *abort_coins;
230 : unsigned int nabort_coins;
231 : char *cr;
232 :
233 2 : as->is = is;
234 2 : pay_cmd = TALER_TESTING_interpreter_lookup_command (is,
235 : as->pay_reference);
236 2 : if (NULL == pay_cmd)
237 0 : TALER_TESTING_FAIL (is);
238 2 : if (GNUNET_OK !=
239 2 : TALER_TESTING_get_trait_proposal_reference (pay_cmd,
240 : &proposal_reference))
241 0 : TALER_TESTING_FAIL (is);
242 2 : if (GNUNET_OK !=
243 2 : TALER_TESTING_get_trait_coin_reference (pay_cmd,
244 : 0,
245 : &coin_reference))
246 0 : TALER_TESTING_FAIL (is);
247 2 : proposal_cmd = TALER_TESTING_interpreter_lookup_command (is,
248 : proposal_reference);
249 :
250 2 : if (NULL == proposal_cmd)
251 0 : TALER_TESTING_FAIL (is);
252 :
253 : {
254 : const json_t *contract_terms;
255 :
256 2 : if (GNUNET_OK !=
257 2 : TALER_TESTING_get_trait_contract_terms (proposal_cmd,
258 : &contract_terms))
259 0 : TALER_TESTING_FAIL (is);
260 : {
261 : /* Get information that needs to be put verbatim in the
262 : * deposit permission */
263 : struct GNUNET_JSON_Specification spec[] = {
264 2 : GNUNET_JSON_spec_string ("order_id",
265 : &order_id),
266 2 : GNUNET_JSON_spec_fixed_auto ("merchant_pub",
267 : &merchant_pub),
268 2 : TALER_JSON_spec_amount_any ("amount",
269 : &total_amount),
270 2 : GNUNET_JSON_spec_end ()
271 : };
272 :
273 2 : if (GNUNET_OK !=
274 2 : GNUNET_JSON_parse (contract_terms,
275 : spec,
276 : &error_name,
277 : &error_line))
278 : {
279 : char *js;
280 :
281 0 : js = json_dumps (contract_terms,
282 : JSON_INDENT (1));
283 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
284 : "Parser failed on %s:%u for input `%s'\n",
285 : error_name,
286 : error_line,
287 : js);
288 0 : free (js);
289 0 : TALER_TESTING_FAIL (is);
290 : }
291 : }
292 : }
293 :
294 2 : cr = GNUNET_strdup (coin_reference);
295 2 : abort_coins = NULL;
296 2 : nabort_coins = 0;
297 2 : if (GNUNET_OK !=
298 2 : build_coins (&abort_coins,
299 : &nabort_coins,
300 : cr,
301 : is))
302 : {
303 0 : GNUNET_array_grow (abort_coins,
304 : nabort_coins,
305 : 0);
306 0 : GNUNET_free (cr);
307 0 : TALER_TESTING_FAIL (is);
308 : }
309 2 : GNUNET_free (cr);
310 :
311 2 : if (GNUNET_OK !=
312 2 : TALER_TESTING_get_trait_h_contract_terms (proposal_cmd,
313 : &h_proposal))
314 0 : TALER_TESTING_FAIL (is);
315 2 : as->oah = TALER_MERCHANT_order_abort (TALER_TESTING_interpreter_get_context (
316 : is),
317 : as->merchant_url,
318 : order_id,
319 : &merchant_pub,
320 : h_proposal,
321 : nabort_coins,
322 : abort_coins,
323 : &abort_cb,
324 : as);
325 2 : GNUNET_array_grow (abort_coins,
326 : nabort_coins,
327 : 0);
328 2 : if (NULL == as->oah)
329 0 : TALER_TESTING_FAIL (is);
330 : }
331 :
332 :
333 : /**
334 : * Free a "pay abort" CMD, and cancel it if need be.
335 : *
336 : * @param cls closure.
337 : * @param cmd command currently being freed.
338 : */
339 : static void
340 2 : abort_cleanup (void *cls,
341 : const struct TALER_TESTING_Command *cmd)
342 : {
343 2 : struct AbortState *as = cls;
344 :
345 2 : if (NULL != as->oah)
346 : {
347 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
348 : "Command `%s' did not complete.\n",
349 : TALER_TESTING_interpreter_get_current_label (
350 : as->is));
351 0 : TALER_MERCHANT_order_abort_cancel (as->oah);
352 : }
353 2 : GNUNET_array_grow (as->acs,
354 : as->acs_length,
355 : 0);
356 2 : GNUNET_free (as);
357 2 : }
358 :
359 :
360 : /**
361 : * Offer internal data useful to other commands.
362 : *
363 : * @param cls closure
364 : * @param[out] ret result (could be anything)
365 : * @param trait name of the trait
366 : * @param index index number of the object to extract.
367 : * @return #GNUNET_OK on success
368 : */
369 : static int
370 0 : abort_traits (void *cls,
371 : const void **ret,
372 : const char *trait,
373 : unsigned int index)
374 : {
375 0 : struct AbortState *as = cls;
376 : struct TALER_TESTING_Trait traits[] = {
377 0 : TALER_TESTING_trait_end ()
378 : };
379 :
380 : (void) as;
381 0 : return TALER_TESTING_get_trait (traits,
382 : ret,
383 : trait,
384 : index);
385 : }
386 :
387 :
388 : struct TALER_TESTING_Command
389 2 : TALER_TESTING_cmd_merchant_order_abort (const char *label,
390 : const char *merchant_url,
391 : const char *pay_reference,
392 : unsigned int http_status)
393 : {
394 : struct AbortState *as;
395 :
396 2 : as = GNUNET_new (struct AbortState);
397 2 : as->http_status = http_status;
398 2 : as->pay_reference = pay_reference;
399 2 : as->merchant_url = merchant_url;
400 : {
401 2 : struct TALER_TESTING_Command cmd = {
402 : .cls = as,
403 : .label = label,
404 : .run = &abort_run,
405 : .cleanup = &abort_cleanup,
406 : .traits = &abort_traits
407 : };
408 :
409 2 : return cmd;
410 : }
411 : }
412 :
413 :
414 : /* end of testing_api_cmd_abort_order.c */
|